From 281284d8aa54454ca7932c6048ca90dc5eac359d Mon Sep 17 00:00:00 2001 From: Robert Ramey Date: Thu, 16 Mar 2017 09:36:41 -0700 Subject: [PATCH] Changes from Steven Watanabe Review of the library. The ones marked *** have been addressed The ones marked ??? (there's only a couple) are still being looked at On 3/6/17 7:35 AM, Steven Watanabe via Boost wrote: > AMDG > > All comments are as of 3bcfabe1cafb8ef6edf08d3bd8730a1555b1c45d > > automatic.hpp > > Actually it looks like calculate_max_t was originally > written to return T or U, but was then altered to > use [u]intmax_t. Right - My intention was that the automatic would reflect the standard C++ promotions but would return the [u]intmax_t . This is later reduced according to the anticipated range of the result. So the whole business is about trying to get the sign right. I made the changes and reran the tests and nothing changed. *** automatic has been altered. It doesn't attempt to use rules from C++. It uses it's own rules as documented in the manual > > 97: > // clause 4 - otherwise use unsigned version of the signed type > std::uintmax_t > It isn't clear to me why uintmax_t is preferable > to intmax_t when neither is able to represent every > possible result. If both operands are signed - then uintmax_t could hold one more bit than uintmax_t can. In these cases the operand result can be twice as large before runtime checking is necessary. *** Simplified and better rules work better now. > 124: > using t_base_type = typename base_type::type; > This is called from safe_base_operations:259, which > has already called base_type. > > From the documentation, I had assumed that automatic > used the range from safe_base. However this range > is not actually passed to the PromotionPolicy, so > automatic always bumps the type to the next largest > integer type, until it reaches [u]intmax_t. This > means than any expressions with more than a few > terms will always be carried out using [u]intmax_t, > which is potentially inefficient, e.g. intmax_t may > require software implementation if it's larger > than the hardware register size. *** code has been altered so that safe range is passed to the all promotion polcies. promotion policies return a numeric type - which is not a safe type - which depends on the particular policy. eg. native returns the result type based on C++ rules while automatic returns a generally larger result type. Clearly this is a sticky point. There are two issues here. One is the result base type and the other is the min/max range of safe result type returned. The return safe type includes the proper range but may have a base type which is larger than necessary. I'll have to look into this. *** I feel that I've got this pinned down now. > A greater > problem arises from the fact that mixed signed/unsigned > arithmetic eventually chooses unsigned when the > range is too large. If we have a slightly complex > expression with both signed and unsigned terms, the > result type will eventually be selected as uintmax_t, > which may be incapable of representing the result, > thus causing an error. *** this can happen. but I believe that the new rules diminish this possibility. Hmmm - My thinking for "automatic" was that it should mimic the "native" C/C++ promotion but return a larger size. I'll have to think about this some more. I was wrong about this. > (I know that this scenario > can also fail with builtin integer types, but > it seems to me that the whole point of 'automatic' > is to make things "just work" without having to > worry about pesky things like signedness) Right. To the extent possible. Looking at this obvervation, I see that native promotion has the same problem in that it's unclear whether it should take safe types or native base types as arguments. More stuff to think about. *** fixed - takes safe types and returns built-in type I'm sure that when I started out, I thought I had it all clear in my mind. But then as I worked through my tests and examples, my "fixed" clouded the issue. > > 247: > 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; > Why division doesn't use calculate_max_t like multiplication > deserves some explanation. Division can overflow > too (numeric_limits::max() / 1). Hmmm - I'm sure it's because I've presumed that the result can aways be contained in the same size type as the number being divide. That t / x <= t - and divide by zero is checked separately. Am I wrong about this? *** addressed by new rules - but numeric_limits::max() / 1 will still overflow because uint/int -> int - but numeric_limits::max() / 1u won't because uint/uint -> uint > > 275: > static constexpr divide( > This function appears to be necessary, but is > not documented as part of the PromotionPolicy > concept. The same goes for modulus. Damn - I forgot about this. It couples the operation with the promotion policy. I spent an incredible amount of time trying to avoid doing this. But in the end I just figure out how to do it. So I had to put these in. ??? - still investigating as to whether this can be eliminated > > 328: > template > struct left_shift_result > Inconsistent with the documantation of PromotionPolicy > which says: PP::left_shift_result::type. Same > for right_shift_result, and bitwise_result. Right *** fixed - improved left shift implementation > > 362: > using type = typename boost::mpl::if_c< > (sizeof(t_base_type) > sizeof(u_base_type)), > t_base_type, > u_base_type > >::type; > Is it actually correct to combine &, |, and ^ > in the same class? The result of & will always > fit in the smaller type, but | and ^ require > the larger type. Right - I'll fix this *** fixed - broke out results for &, |, and ^ > > checked.hpp: > > 90: > // INT32-C Ensure that operations on signed > // integers do not overflow > This is the unsigned case. *** fixed > > 220: > template > constexpr checked_result add( > Consider the following (assuming 2's complement): > checked::add(INT_MIN, UINT_MAX) > The result should be INT_MAX, *** now it is on both automatic promotion polcies I'm not seeing this. With 8 bit ints we've got INT_MIN = x80 = -128 UINT_MAX = x80 = 128 adding together x100 truncating = 0 which might be OK except on current machines but does violate the standard in that it undefined behavior. Given that compiler optimizers can do anything they wand on UB I don't think we can do this but your implementation > will generate an error when converting UINT_MAX > to int. As I believe it should - UINT_MAX = 0x80 convert to int - nothing changes x80 interpreted as an int is -128 So this would transform an arithmetic value of 128 to a value of -128. Not a good thing. What I've done is applied the standard. Convert each type to the result type then do the addition. On this conversion we change the value - game over - invoke error. > 227: > if(! rt.no_exception() ) > The double negative makes this confusing to read. *** fixed > > 230: > return detail::add(t, u); > This relies on implicit conversion and may generate > spurious warnings. conversion - I don't see it. detail::add(t, u) return a checked_result which is returned as another checked_result. No conversion. Probably not even a copy with copy elusion. *** still not seeing a problem here > > 301: > Everything I said about add also applies to subtract. OK - if we can agree on add we'll agree on subtract. > > 411: > exception_type::underflow_error, > This should be overflow_error. OK > > 467: > Again as with add. This time the problematic case is > multiply(-1, -1) again - same response > > 539: > return detail::divide(tx.m_r, ux.m_r); > Just because m_r is public, doesn't mean that > you should access it directly. OK > > 583: > constexpr divide_automatic( > The difference between divide and divide_automatic > could use some explanation. It looks like the only > difference is that divide_automatic doesn't cast > the divisor, but it isn't clear why. Also, why > do you need two checked divide functions in the > first place? There should really just be one > that always works correctly. LOL - that's what I thought. I couldn't make one which worked for both native and automatic promotions. The case which is giving me fits is dividing INT_MIN / -1 INT_MIN = 0x80 = -128 -128 / -1 = + 128 = 0x80 = INT_MIN again. So we have INT_MIN / -1 -> INT_MIN an incorrect result. Now I forget - but this is only a problem with automatic promotions. ??? need to check this again > > 622: > return cast(abs(t) % abs(u)); > This likely differs from the builtin % and should be documented > as such. Hmmm - slightly sticky issue here. I used from &5.6/4 If both operands are nonnegative then the remainder is nonnegative; if not, the sign of the remainder is implementation-defined. from ISO14882:2003(e) is no longer present in ISO14882:2011(e) But even so my implementation should have trapped on negative operands. I'll consider what to do about this. Also I prefer % to have the property that > t / u * u + t % u = t, which this implementation > does not satisfy. I think this can be achieved *** I think I've fixed this. > 639: > // INT34-C C++ standard paragraph 5.8 > if(u > std::numeric_limits::digits){ > You're off-by-one > "The behavior is undefined if the right operand > is negative, or greater than *or equal to* the length in > bits of the promoted left operand" (emphasis added) > > left_shift and right_shift (625-792) have several problems: > - 640: You're checking the width of T even though the > actual shift is done after casting to R. > - 696, 708: These overloads serve no purpose. The > only difference between them is checks that > are already handled by check_shift. > - 785: This test duplicates work done be check_shift *** I've pretty much totally redid left and right shift last weekend. I think they are more likely to be correct here. > > checked_result.hpp: > > 36: // can't select constructor based on the current status of another > > // checked_result object. So no copy constructor > Saying that there is no copy constructor is misleading, > as the default cctor exists and is used. *** fixed OK > 67: > //assert(no_exception()); > Why is this commented out? reading a > not-currently-active member of a union > is undefined behavior. the assert conflicts with constexpr so I can't use it. When one tries to cast to an R a compile time - it's trapped just fine by my clang compiler on my mac. The second case seems to pass because I never retrieve the error message at compile time. > > 99: > constexpr boost::logic::tribool > operator<=(const checked_result & t) const { > return ! operator>(t) && ! operator<(t); > } > This is opertor==. The correct way is just ! operator>(t). OK *** fixed > > cpp.hpp: > > 54: > using rank = > Rank is only used for comparisons. I don't see > any reason to use instead of using sizeof directly. > This applies to automatic::rank as well. > (Note: rank matters for builtin integer promotion, > because types with the same size can have different > ranks. Your implementation of rank, however, is > strictly based on size, making it essentially useless.) Then I should probably change the implementation so that types of different rank but the same size (e.g. int and long on some machines) are handled correctly. I don't think this is too hard to fix. *** fixed > > exception.hpp: > > 15: > // contains operations for doing checked aritmetic on NATIVE > > // C++ types. > Wrong file. Also s/aritmetic/arithmetic/ for > wherever this was pasted from. OK *** fixed > > exception_policies.hpp: > > 1: > #ifndef BOOST_NUMERIC_POLICIES_HPP > #define BOOST_NUMERIC_POLICIES_HPP > Make this BOOST_NUMERIC_EXCEPTION_POLICIES_HPP? *** fixed > > interval.hpp: > > 18: > #include // quick_exit > I don't see quick_exit anywhere in this file. *** fixed > > 87: > // account for the fact that for floats and doubles > There's also long double, you know. OK *** fixed > > 101: > namespace { > Please don't use the unnamed namespace in headers. OK - but I forgot why it's not recommended *** fixed > > 133: > > template > constexpr bool less_than( > const checked_result & lhs, > const checked_result & rhs > Why is this here and not in checked_result.hpp? *** Removed > Also this can be implemented easily using operator<: > return lhs < rhs; > (The implicit conversion from tribool to bool > does exactly what you want here.) OK - I'll take your word for it. used lhs < rhs in a lambda *** LOL - lamdas can't be constexper ! So I just eliminated this and used safe_compare directly. Things should be clearer now > > 257: > checked::modulus(t.l, u.l), > checked::modulus(t.l, u.u), > checked::modulus(t.u, u.l), > checked::modulus(t.u, u.u) > This appears to be copied from divide, but it's totally > wrong. modulus is not monotonic, so you can't get > away with checking only the boundaries. *** I spent significant time looking into the mod operator at all levels. I'm pretty convinced that the current implementation (which is pretty similar to the old one - but simpler is indeed correct. It handles cases where the divisor includes zero and correctly handles the sign > 339: > const R rl = safe_compare::greater_than(t.l, u.l) ? t.l : u.l; > The implicit cast to R may not be safe. > (same at 340, 356, and 357) *** replaced with checked cast. > 359: > if(safe_compare::greater_than(rl, ru)){ > This check isn't necessary. The union of two non-empty > intervals can't be empty. OK *** fixed > > 453: > template<> > std::ostream & operator<<(std::ostream & os, const > boost::numeric::interval & i) > - This specialization is not a template, and can > only appear in one translation unit. Please expand upon this > - The implementation requires , but you only #include Right = but any other which invokes this function will aready have included ostream. That's why included rather than since this operator is used almost exclusively for debugging/examples. *** replaced ostream with basic_ostream > native.hpp: > > 27: > // 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. > Comment out-dated. The code (somewhat) uses decltype on the > correct operators. *** fixed > safe_base.hpp: > > 240: > safe_base operator++(){ // pre increment > pre-increment and pre-decrement should return by reference. > post-increment and post-decrement should return by value. > You currently have everything returning by value > except post-decrement (which returns a dangling > reference) Right *** fixed > > 258: > constexpr safe_base operator-() const { // unary minus > should unary minus allow unsigned to become signed? (subject to > the PromotionPolicy, of course). LOL - I struggled with this - switched a couple of times back and forth. I decided to leave the type the same. That probably conflicts with automatic promotion. This still needs thinking about. *** 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. 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 > 261: > constexpr safe_base operator~() const { > This will fail if Min/Max are anything other than > the full range of the Stored type. *** This never occurred to me. It's a common case. It really highlights the fact that this operation is not really an arithmetic operation. It's probably a good thing that safe numerics will trap in most cases. > > safe_base_operations.hpp: > > 82: > indeterminate(t_interval < this_interval), > This works, but it would be a bit more intuitive > if interval had an 'intersects' function. *** H:mmmm it has intersection. Would the following be better? static_assert( intersection(t_interval, this_interval).exception(), "safe type cannot be constructed with this type" ); > > 244: > // 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 > Unfortunately, ADL casts a very wide net. > What makes these operators (mostly) safe > is the use of enable_if. *** changed to: // Note: the following global operators will be found via // argument dependent lookup. > > 267: > // Use base_value(T) ( which equals MIN ) to create a new interval. Same > // for MAX. Now > Now ... what? > > 288: > constexpr static const interval type_interval = > exception_possible() ? > interval{} > This can overestimate the result interval if the > interval only overflows on one side. ??? LOL - I never thought about that refinement. I'll have to think about this. I'm thinking that this might well apply to most of the operations. > > 313: > using type_helper = typename boost::mpl::if_c< > std::numeric_limits::is_integer, > safe_type, > unsafe_type > >::type; > This is what? Support for floating point in the future? ??? I think I put this in because of the possibility of safe x = 10; float y = 10.0 auto z = x + y; so z would be a float Of course now I don't actually remember. As I write this I don't feel confident that I actually know what the above will do. Though I'm sure I had an idea at one time. I'll take another look. > > 531: > // when we add the temporary intervals above, we'll get a new > interval > // with the correct range for the sum ! > I think you mean multiply and product, not add and sum. > *** of course > 676: > constexpr static const interval type_interval = > exception_possible() ? > At this point, you lose any benefit of divide_nz over divide. > You should check r_interval.exception() instead of exception_possible(). > The same goes for modulus. ??? Hmmm - still looking at this. > > 748: > // argument dependent lookup should guarentee that we only get here > I don't understand this comment. ** removed LOL - that makes two of us. > > 867: > static_cast(base_value(t)) > > % static_cast(base_value(u)) > Okay, we have a problem here: checked::modulus does /not/ > have the same behavior as the builtin % when the divisor > is negative, which means that treating them as interchangable > here will result in weird and inconsistent behavior (not to > mention silently producing values outside the expected interval.) *** fixed - I think > > 907: > typename boost::lazy_enable_if_c< > ..., > boost::mpl::identity > enable_if_c with bool should work just as well. ** fixed > > 932: > // if the ranges don't overlap > (! boost::logic::indeterminate(r)) ? > // values in those ranges can't be equal > false > This is operator<, not operator==. You should return r, not false here. > Same at 970 in operator>. *** fixed Also, you're already implementing > operator<= and >= in terms of < and >. Is there a reason > to implement > separately? (t > u) == (u < t) *** fixed > > 1221: > // handle safe << int, int << safe, safe << safe > // exclude std::ostream << ... > Copy/paste. Should be >> and istream. *** fixed > > 1276: > > base_value(std::numeric_limits::max()) > This doesn't take the input range into account ??? I think it does > can drastically overestimate the result range. ??? don't think so - unless one is referring to the Min value > > 1401: > using bwr = bitwise_or_result; > I think it would be slightly better to create an alias > template > using bitwise_xor_result = bitwise_or_result; > instead of using bitwise_or_result directly in > the implementation of xor. (Note that the range > of ^ can be larger than that of | if the lower bound > of the parameters is greater than 0.) > > 1432, 1474: > class P, // promotion polic > s/polic/policy/ > > 1435: > std::ostream & operator<<( > std::ostream & os, > Should this use std::basic_ostream? I think so too. *** done > > safe_common.hpp: > > safe_compare.hpp: > > safe_integer.hpp: > > safe_literal.hpp: > > 111: > constexpr safe_literal_impl operator-() const { // unary minus > Err, what? This will work iff. N is 0. that's out > > 140: > #define safe_literal(n) \ > This is not acceptable. It totally violates > the naming rules for macros. I'm still struggling with this. What do you suggest? > > safe_range.hpp: > > utility.hpp: > > 26: > constexpr log(T x){ > A function with a common name, that is not in a private > namespace, and can match almost anything is a really > bad idea. Hmmm I could change this to something like bit_count > > concept/exception_policy.hpp: > > The reason that the check is commented out deserves > explanation. (Missing functions are acceptible and > will cause a compile-time error instead of checking > at runtime.) > > concept/*.hpp: no comments > > In Christ, > Steven Watanabe > > > _______________________________________________ > Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost Misc cpp / automatic policies - don't address floating point > --- doc/boostbook/automatic.xml | 73 +++- doc/boostbook/checked_result.xml | 15 +- doc/boostbook/cpp.xml | 14 +- doc/boostbook/eliminate_runtime_penalty.xml | 26 +- doc/boostbook/exception_policy_concept.xml | 18 +- doc/boostbook/exception_type.xml | 2 +- doc/boostbook/faq.xml | 10 +- doc/boostbook/native.xml | 6 +- doc/boostbook/no_exception_support.xml | 61 ++-- doc/boostbook/promotion_policy_concept.xml | 53 +-- doc/boostbook/safe.xml | 45 ++- doc/boostbook/safe_introduction.xml | 19 +- doc/boostbook/safe_literal.xml | 6 +- doc/boostbook/safe_numeric_concept.xml | 113 +++++-- doc/boostbook/safe_numerics.xml | 6 +- doc/boostbook/safe_range.xml | 38 ++- doc/boostbook/trap_exception.xml | 2 +- doc/boostbook/tutorial.xml | 20 +- examples/Motor.c | 1 - examples/example10.cpp | 2 +- examples/example13.cpp | 2 +- examples/example83.cpp | 4 +- examples/example84.cpp | 2 +- examples/example91.cpp | 2 +- include/automatic.hpp | 315 ++++++----------- include/checked.hpp | 40 ++- include/checked_result.hpp | 69 ++-- include/concept/exception_policy.hpp | 2 + include/concept/promotion_policy.hpp | 4 +- include/cpp.hpp | 36 +- include/exception.hpp | 4 +- include/exception_policies.hpp | 12 +- include/interval.hpp | 134 ++++---- include/native.hpp | 15 +- include/safe_base.hpp | 101 +++--- include/safe_base_operations.hpp | 356 +++++++------------- include/safe_common.hpp | 14 - include/safe_integer.hpp | 4 +- include/safe_literal.hpp | 33 +- include/utility.hpp | 50 +++ test/test_add.hpp | 35 +- test/test_add_automatic.cpp | 41 ++- test/test_auto.cpp | 2 +- test/test_checked_add.cpp | 2 +- test/test_checked_divide_automatic.cpp | 4 +- test/test_checked_divide_native.cpp | 2 +- test/test_checked_result.cpp | 2 +- test/test_checked_subtract.cpp | 2 +- test/test_compare.cpp | 2 +- test/test_divide_automatic.cpp | 2 +- test/test_divide_native.cpp | 2 +- test/test_interval.cpp | 4 +- test/test_left_shift.hpp | 2 +- test/test_modulus.hpp | 75 +++-- test/test_multiply.hpp | 12 - test/test_multiply_automatic.cpp | 34 +- test/test_multiply_native.cpp | 2 +- test/test_right_shift.hpp | 2 +- test/test_subtract.hpp | 2 +- test/test_subtract_automatic.cpp | 1 - test/test_z.cpp | 83 +++++ 61 files changed, 1093 insertions(+), 949 deletions(-) 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(){