diff --git a/doc/boostbook/tutorial.xml b/doc/boostbook/tutorial.xml
index e704811..84d9df4 100644
--- a/doc/boostbook/tutorial.xml
+++ b/doc/boostbook/tutorial.xml
@@ -203,8 +203,8 @@ detected error:converted negative value to unsigned
Programming by Contract is Too Slow
Programming by Contract is a highly regarded technique. There has
- been much written about it has been proposed as an addition to the C++
- language GarciaCrowl &
+ been much written about it and it has been proposed as an addition to the
+ C++ language GarciaCrowl &
Ottosen. It (mostly) depends upon runtime checking of parameter
and object values upon entry to and exit from every function. This can
slow the program down considerably which in turn undermines the main
@@ -224,10 +224,10 @@ detected error:converted negative value to unsigned
In the example above the function convert incurs significant runtime
cost every time the function is called. By using "safe" types, this cost
- is moved to moment when the parameters are constructed. Depending on how
- the program is constructed, this may totally eliminate extraneous
- computations for parameter requirement type checking. In this scenario,
- there is no reason to suppress the checking for release mode and our
- program can be guaranteed to be always arithmetically correct.
+ is moved to moment when the parameters are constructed. This may totally
+ eliminate extraneous computations for parameter requirement type checking.
+ In this scenario, there is no reason to suppress the checking for release
+ mode and our program can be guaranteed to be always arithmetically
+ correct.
diff --git a/examples/18F252_desktop.h b/examples/18F252_desktop.h
index fe0ecff..1906da7 100644
--- a/examples/18F252_desktop.h
+++ b/examples/18F252_desktop.h
@@ -80,17 +80,16 @@ void ftest(){
return;
}
-
#if ! defined(literal)
// define a macro for literal types. This may not be strictly necessary
// but it provides more information at compile time to the safe numerics
// library which may result in faster code.
-//#define literal(x) boost::numeric::safe_unsigned_literal{}
-#define literal(x) x
+#define literal(x) boost::numeric::safe_unsigned_literal{}
+//#define literal(x) x
#endif
// make a 16 bit value from two 8 bit ones
-int16 inline make16(int8 h, int8 l){
+uint16 inline make16(uint8 h, uint8 l){
return (h << literal(8)) | l;
}
diff --git a/examples/example1.cpp b/examples/example1.cpp
index 35f9ba1..c1a1365 100644
--- a/examples/example1.cpp
+++ b/examples/example1.cpp
@@ -28,7 +28,7 @@ int main(int argc, const char * argv[]){
std::cout << "Using safe numerics" << std::endl;
try{
using namespace boost::numeric;
- safe x = 127;
+ safe x = INT_MAX;
safe y = 2;
safe z;
// rather than producing and invalid result an exception is thrown
diff --git a/examples/example7.cpp b/examples/example7.cpp
index 9ce3b0f..a587697 100644
--- a/examples/example7.cpp
+++ b/examples/example7.cpp
@@ -7,7 +7,7 @@
// NOT using safe numerics - enforce program contract explicitly
// return total number of minutes
-unsigned int convert(
+unsigned int contract_convert(
const unsigned int & hours,
const unsigned int & minutes
) {
@@ -26,90 +26,56 @@ unsigned int convert(
// define convenient typenames for hours and minutes hh:mm
using hours_t = boost::numeric::safe_unsigned_range<0, 23>;
using minutes_t = boost::numeric::safe_unsigned_range<0, 59>;
+using minutes_total_t = boost::numeric::safe_unsigned_range<0, 59>;
// return total number of minutes
// type returned is safe_unsigned_range<0, 24*60 - 1>
-auto safe_convert(const hours_t & hours, const minutes_t & minutes) {
- // no need for checking as parameters are guaranteed to be within limits
- // expression below cannot throw ! zero runtime overhead
+auto convert(const hours_t & hours, const minutes_t & minutes) {
+ // no need to test pre-conditions
+ // input parameters are guaranteed to hold legitimate values
+ // no need to test post-conditions
+ // return value guaranteed to hold result
return hours * 60 + minutes;
}
+unsigned int test(
+ unsigned int hours,
+ unsigned int minutes
+){
+ // problem: checking of externally produced value can be expensive
+ // invalid parameters - detected - but at a heavy cost
+ return contract_convert(hours, minutes);
+
+ // solution: use safe numerics
+ // safe types can be implicitly constructed base types
+ // construction guarentees corectness
+ // return value is known to fit in unsigned int
+ return convert(hours, minutes);
+
+ // actually we don't even need the convert function any more
+ return hours_t(hours) * 60 + minutes_t(minutes);
+}
+
int main(int argc, const char * argv[]){
std::cout << "example 8: ";
std::cout << "enforce contracts with zero runtime cost" << std::endl;
- std::cout << "Not using safe numerics" << std::endl;
- // problem: checking of externally produced value can be expensive
- try {
- convert(10, 83); // invalid parameters - detected - but at a heavy cost
- }
- catch(std::exception e){
- std::cout << "exception thrown for parameter error" << std::endl;
- }
-
- // solution: use safe range to restrict parameters
- std::cout << "Using safe numerics" << std::endl;
+ unsigned int total_minutes;
try {
- // parameters are guaranteed to meet requirements
- hours_t hours(10);
- minutes_t minutes(83); // interrupt thrown here
- // so the following will never fail
- safe_convert(hours, minutes);
+ total_minutes = test(17, 83);
}
catch(std::exception e){
- std::cout
- << "exception thrown when invalid arguments are constructed"
- << std::endl;
+ std::cout << "parameter error detected" << std::endl;
}
try {
- // parameters are guaranteed to meet requirements when
- // constructed on the stack
- safe_convert(hours_t(10), minutes_t(83));
+ total_minutes = test(17, 10);
}
catch(std::exception e){
- std::cout
- << "exception thrown when invalid arguments are constructed on the stack"
- << std::endl;
+ // should never arrive here
+ std::cout << "parameter error erroneously detected" << std::endl;
+ return 1;
}
-
- try {
- // parameters are guaranteed to meet requirements when
- // implicitly constructed to safe types to match function signature
- safe_convert(10, 83);
- }
- catch(std::exception e){
- std::cout
- << "exception thrown when invalid arguments are implicitly constructed"
- << std::endl;
- }
-
- try {
- // the following will never throw as the values meet requirements.
- const hours_t hours(10);
- const minutes_t minutes(17);
-
- // note zero runtime overhead once values are constructed
-
- // the following will never throw because it cannot be called with
- // invalid parameters
- safe_convert(hours, minutes); // zero runtime overhead
-
- // since safe types can be converted to their underlying unsafe types
- // we can still call an unsafe function with safe types
- convert(hours, minutes); // zero (depending on compiler) runtime overhead
-
- // since unsafe types can be implicitly converted to corresponding
- // safe types we can just pass the unsafe types. checking will occur
- // when the safe type is constructed.
- safe_convert(10, 17); // runtime cost in creating parameters
-
- }
- catch(std::exception e){
- std::cout << "error detected!" << std::endl;
- }
-
return 0;
}
diff --git a/include/automatic.hpp b/include/automatic.hpp
index 1533e64..4a352a9 100644
--- a/include/automatic.hpp
+++ b/include/automatic.hpp
@@ -21,6 +21,7 @@
#include // (u)intmax_t,
#include // true_type, false_type, is_same
#include
+#include
#include "utility.hpp"
#include "safe_common.hpp"
@@ -325,17 +326,108 @@ 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;
+
+ 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;
+ 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())
+ base_value(std::numeric_limits::min()),
+ base_value(std::numeric_limits::max())
};
+
+ constexpr static const interval u_interval{
+ base_value(std::numeric_limits::min()),
+ base_value(std::numeric_limits::max())
+ };
+
+ constexpr static const checked_result> r_interval =
+ left_shift(t_interval, u_interval);
+
+ constexpr static const interval result_interval =
+ r_interval.no_exception() ?
+ static_cast>(r_interval) :
+ interval()
+ ;
+
+ constexpr static const std::uintmax_t upper_bound =
+ r_interval.no_exception() ?
+ static_cast>(r_interval).u :
+ std::numeric_limits::max();
+
+ constexpr static const std::uintmax_t lower_bound =
+ r_interval.no_exception() ?
+ static_cast>(r_interval).l :
+ std::numeric_limits::min();
+
+ using r_base_type = typename boost::mpl::if_c<
+ r_interval.no_exception(),
+ typename boost::numeric::unsigned_stored_type<
+ lower_bound,
+ upper_bound
+ >,
+ std::uintmax_t
+ >::type;
+
using type = typename result_type<
- T,
- t_interval.l,
- t_interval.u
+ r_base_type,
+ lower_bound,
+ upper_bound
>::type;
};
diff --git a/include/checked.hpp b/include/checked.hpp
index 7ab06ef..d1874ea 100644
--- a/include/checked.hpp
+++ b/include/checked.hpp
@@ -627,61 +627,83 @@ constexpr modulus(
namespace detail {
+// INT34-C C++
+
+// standard paragraph 5.8 / 2
+// The value of E1 << E2 is E1 left-shifted E2 bit positions;
+// vacated bits are zero-filled.
template
typename std::enable_if<
- ! std::numeric_limits::is_signed,
+ // If E1 has an unsigned type
+ ! std::numeric_limits::is_signed,
checked_result
>::type
-constexpr check_shift(
+constexpr checked_left_shift(
const T & t,
const U & u
-) {
- // INT34-C C++ standard paragraph 5.8
- if(u > std::numeric_limits::digits){
+) noexcept {
+ // the value of the result is E1 x 2^E2, reduced modulo one more than
+ // the maximum value representable in the result type.
+ /*
+ if(u >= std::numeric_limits::digits){
+ return cast(0);
+ }
+ */
+ // note: we are intentionally varying from the standards language here.
+ // a safe is meant to be value preserving. That is t << u should
+ // equal an arithmetically correct value or fail in some visible manner.
+ // So we're going to fail if are shift loses higher order bits.
+ //
+ // note: there is a good argument for following exactly the standards
+ // language above. Reasonable people can disagree.
+
+ if(u == 0){
+ // behavior is undefined
+ return checked_result(
+ exception_type::domain_error,
+ "shifting all bits off the left is undefined behavior"
+ );
+ }
+
+ const int max_shift = std::numeric_limits::digits - u;
+
+ if(max_shift < 0
+ || u >= max_shift
+ || t >= (cast(1) << max_shift)
+ ){
+ // behavior is undefined
return checked_result(
exception_type::domain_error,
"shifting more bits than available is undefined behavior"
);
}
- // the following are prohibited by the standard. However
- // on all known modern machines with will yield the correct
- // result. I'll err on the conservative side and trap this
- // as undefined behavior. But someone is going to complain
- if(t < 0){
- return checked_result(
- exception_type::domain_error,
- "shifting a negative value is undefined behavior"
- );
- }
- return cast(t);
+
+ return cast(t) << u;
}
template
typename std::enable_if<
- std::numeric_limits::is_signed,
+ // if E1 has a signed type
+ std::numeric_limits::is_signed,
checked_result
>::type
-constexpr check_shift(
+constexpr checked_left_shift(
const T & t,
const U & u
) {
- // INT34-C and C++ standard paragraph 5.8
- if(std::numeric_limits::max() > std::numeric_limits::digits){
- if(u > std::numeric_limits::digits){
- return checked_result(
- exception_type::domain_error,
- "shifting more bits than available is undefined behavior"
- );
- }
- }
- // the following are prohibited by the standard. However
- // on all known modern machines with will yield the correct
- // result. I'll err on the conservative side and trap this
- // as undefined behavior. But someone is going to complain
- if(u < 0){
+ // and E1 x 2^E2 is representable in the corresponding
+ // unsigned type of the result type,
+
+ const int max_shift = std::numeric_limits::digits - u - 1;
+
+ if(max_shift < 0
+ || u >= max_shift
+ || t >= (cast(1) << max_shift)
+ ){
+ // behavior is undefined
return checked_result(
exception_type::domain_error,
- "shifting negative amount is undefined behavior"
+ "shifting more bits than available is undefined behavior"
);
}
if(t < 0){
@@ -690,59 +712,8 @@ constexpr check_shift(
"shifting a negative value is undefined behavior"
);
}
- return cast(t);
-}
-
-template
-typename std::enable_if<
- ! std::numeric_limits::is_signed,
- checked_result
->::type
-constexpr left_shift(
- const R & r,
- const U & u
-){
- return static_cast(r << u);
-}
-
-template
-typename std::enable_if<
- std::numeric_limits::is_signed,
- checked_result
->::type
-constexpr left_shift(
- const R & r,
- const U & u
-){
- // INT13-C. Use bitwise operators only on unsigned operands
- // cannot shift negative values to the left
- return (r < 0) ?
- checked_result(
- exception_type::domain_error,
- "shifting negative values off left is undefined"
- )
- :
- checked_result(r << u)
- ;
-
- /*
- // if a negative value is shifted left, then it could all of
- // a sudden change sign - we inhibit this here.
- if(r < 0){
- U ui = u;
- R ri = r;
- while(ui-- > 0){
- ri <<= 1;
- if(ri >= 0){
- return checked_result(
- exception_type::domain_error,
- "shifting negative values off left is undefined"
- );
- }
- }
- }
- return r;
- */
+ // shift and convert the resulting value to the result type
+ return cast(t) << u;
}
} // detail
@@ -752,43 +723,92 @@ template
constexpr checked_result left_shift(
const T & t,
const U & u
-) {
- // INT13-C Note: We don't enforce recommendation as acually written
- // as it would break too many programs. Specifically, we permit signed
- // integer operands but require that they not be negative. This we can only
- // enforce at runtime.
-
- const checked_result rx = detail::check_shift(t, u);
-
- if(! rx.no_exception())
- return rx;
-
- return detail::left_shift(rx, u);
+){
+ // INT13-C Note:
+ // on all known modern machines with will yield the correct
+ // result. I'll err on the conservative side and trap this
+ // as undefined behavior. But someone is going to complain
+ if(u < 0){
+ return checked_result(
+ exception_type::domain_error,
+ "shifting negative amount is undefined behavior"
+ );
+ }
+ if(t == 0)
+ return cast(0);
+ return detail::checked_left_shift(t, u);
}
+namespace detail {
+
+// standard paragraph 5.8 / 3
+// The value of E1 >> E2 is E1 right-shifted E2 bit positions;
+template
+typename std::enable_if<
+ // If E1 has an unsigned type
+ ! std::numeric_limits::is_signed,
+ checked_result
+>::type
+constexpr checked_right_shift(
+ const T & t,
+ const U & u
+) noexcept {
+ // the value of the result is E1 / 2^E2, reduced modulo one more than
+ // the maximum value representable in the result type.
+ if(u > std::numeric_limits::digits){
+ return cast(0);
+ }
+ return cast(t >> u);
+}
+
+template
+typename std::enable_if<
+ // or if E1 has a signed type
+ std::numeric_limits::is_signed,
+ checked_result
+>::type
+constexpr checked_right_shift(
+ const T & t,
+ const U & u
+) {
+ // and a non-negative value
+ if(t < 0){
+ return checked_result(
+ exception_type::domain_error,
+ "shifting a negative value is undefined behavior"
+ );
+ }
+
+ if(u >= std::numeric_limits::digits){
+ return checked_result(
+ exception_type::domain_error,
+ "shifting more bits than argument size is an error"
+ );
+ }
+ // the value is the integral part of E1 / 2^E2,
+ return cast(t) >> u;
+}
+
+} // detail
+
// right shift
template
constexpr checked_result right_shift(
const T & t,
const U & u
) {
- // INT13-C Note: We don't enforce recommendation as acually written
- // as it would break too many programs. Specifically, we permit signed
- // integer operand but require that it not be negative. This we can only
- // enforce at runtime.
-
- const checked_result rx = detail::check_shift(t, u);
-
- if(! rx.no_exception())
- return rx;
-
- if(u > std::numeric_limits::digits){
+ // on all known modern machines with will yield the correct
+ // result. I'll err on the conservative side and trap this
+ // as undefined behavior. But someone is going to complain
+ if(u < 0){
return checked_result(
exception_type::domain_error,
- "shifting more bits than available is undefined behavior"
+ "shifting negative amount is undefined behavior"
);
}
- return static_cast(rx) >> u;
+ if(t == 0)
+ return cast(0);
+ return detail::checked_right_shift(t, u);
}
///////////////////////////////////
diff --git a/include/safe_base.hpp b/include/safe_base.hpp
index fd5bfa4..56c3ed0 100644
--- a/include/safe_base.hpp
+++ b/include/safe_base.hpp
@@ -257,9 +257,12 @@ public:
constexpr auto operator-() const { // unary minus
return 0 - *this;
}
+ constexpr auto operator+() const { // unary plus
+ return *this;
+ }
template
- constexpr auto operator~() const { // unary minus
- return ~Stored(0) ^ *this;
+ constexpr auto operator~() const { // complement
+ return ~Stored(0u) ^ *this;
}
};
diff --git a/include/safe_base_operations.hpp b/include/safe_base_operations.hpp
index 33d6bdf..07162de 100644
--- a/include/safe_base_operations.hpp
+++ b/include/safe_base_operations.hpp
@@ -251,7 +251,6 @@ struct common_promotion_policy {
template
struct addition_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;
@@ -292,6 +291,7 @@ struct addition_result {
static_cast>(r_interval)
;
+ using exception_policy = typename common_exception_policy::type;
struct safe_type {
using type = safe_base<
result_base_type,
@@ -379,7 +379,6 @@ constexpr inline operator+=(T & t, const U & u){
template
struct subtraction_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;
@@ -415,6 +414,7 @@ struct subtraction_result {
static_cast>(r_interval)
;
+ using exception_policy = typename common_exception_policy::type;
struct safe_type {
using type = safe_base<
result_base_type,
@@ -502,7 +502,6 @@ constexpr inline operator-=(T & t, const U & u){
template
struct multiplication_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;
@@ -512,6 +511,7 @@ struct multiplication_result {
u_base_type
>::type;
+ using exception_policy = typename common_exception_policy::type;
struct safe_type {
// filter out case were overflow cannot occur
// note: subtle trickery. Suppose t is safe_range. Then
@@ -635,7 +635,6 @@ constexpr inline operator*=(T & t, const U & u){
template
struct division_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;
@@ -645,6 +644,7 @@ struct division_result {
u_base_type
>::type;
+ using exception_policy = typename common_exception_policy::type;
struct safe_type {
constexpr static const interval t_interval{
base_value(std::numeric_limits::min()),
@@ -773,7 +773,6 @@ constexpr inline operator/=(T & t, const U & u){
template
struct modulus_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;
@@ -784,6 +783,7 @@ struct modulus_result {
>::type;
+ using exception_policy = typename common_exception_policy::type;
struct safe_type {
constexpr static const interval t_interval{
base_value(std::numeric_limits::min()),
@@ -813,6 +813,7 @@ struct modulus_result {
:
static_cast>(r_interval)
;
+
using type = safe_base<
result_base_type,
type_interval.l,
@@ -1045,7 +1046,6 @@ constexpr operator<=(const T & lhs, const U & rhs) {
// left shift
template
struct left_shift_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;
@@ -1080,6 +1080,7 @@ struct left_shift_result {
static_cast>(r_interval)
;
+ using exception_policy = typename common_exception_policy::type;
using type = safe_base<
result_base_type,
type_interval.l,
@@ -1124,6 +1125,13 @@ typename boost::lazy_enable_if_c<
>::type
constexpr inline operator<<(const T & t, const U & u){
// INT13-CPP
+ // C++ standards document N4618 & 5.8.2
+ static_assert(
+ std::numeric_limits::is_integer, "shifted value must be an integer"
+ );
+ static_assert(
+ std::numeric_limits::is_integer, "shift amount must be an integer"
+ );
using lsr = left_shift_result;
return typename lsr::type(
lsr::return_value(
@@ -1150,7 +1158,6 @@ constexpr inline operator<<=(T & t, const U & u){
// right shift
template
struct right_shift_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;
@@ -1185,6 +1192,7 @@ struct right_shift_result {
static_cast>(r_interval)
;
+ using exception_policy = typename common_exception_policy::type;
using type = safe_base<
result_base_type,
type_interval.l,
@@ -1229,6 +1237,12 @@ typename boost::lazy_enable_if_c<
>::type
constexpr inline operator>>(const T & t, const U & u){
// INT13-CPP
+ static_assert(
+ std::numeric_limits::is_integer, "shifted value must be an integer"
+ );
+ static_assert(
+ std::numeric_limits::is_integer, "shift amount must be an integer"
+ );
using rsr = right_shift_result;
return typename rsr::type(
rsr::return_value(
@@ -1255,10 +1269,16 @@ 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 {
- 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;
@@ -1278,6 +1298,7 @@ struct bitwise_or_result {
base_value(std::numeric_limits::max())
;
+ using exception_policy = typename common_exception_policy::type;
using type = safe_base<
result_base_type,
0,
@@ -1295,6 +1316,14 @@ typename boost::lazy_enable_if_c<
bitwise_or_result
>::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;
@@ -1362,6 +1391,14 @@ typename boost::lazy_enable_if_c<
bitwise_and_result
>::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;
@@ -1398,6 +1435,14 @@ typename boost::lazy_enable_if_c<
bitwise_or_result
>::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 result_base_type = typename bwr::result_base_type;
diff --git a/include/safe_literal.hpp b/include/safe_literal.hpp
index ada1b03..13d6c36 100644
--- a/include/safe_literal.hpp
+++ b/include/safe_literal.hpp
@@ -112,8 +112,8 @@ using safe_signed_literal = safe_literal_impl<
template<
std::uintmax_t N,
- class P = native,
- class E = throw_exception
+ class P = void,
+ class E = void
>
using safe_unsigned_literal = safe_literal_impl<
typename boost::numeric::unsigned_stored_type,
@@ -132,4 +132,32 @@ using safe_unsigned_literal = safe_literal_impl<
} // numeric
} // boost
+/////////////////////////////////////////////////////////////////
+// numeric limits for safe etc.
+
+#include
+
+namespace std {
+
+template<
+ typename T,
+ T N,
+ class P,
+ class E
+>
+class numeric_limits >
+ : public std::numeric_limits
+{
+ using SL = boost::numeric::safe_literal_impl;
+public:
+ constexpr static SL min() noexcept {
+ return SL();
+ }
+ constexpr static SL max() noexcept {
+ return SL();
+ }
+};
+
+} // std
+
#endif // BOOST_NUMERIC_SAFE_LITERAL_HPP
diff --git a/include/utility.hpp b/include/utility.hpp
index 7b7ecab..298c76c 100644
--- a/include/utility.hpp
+++ b/include/utility.hpp
@@ -18,21 +18,24 @@
namespace boost {
namespace numeric {
+ // the number of bits required to render the value in x
template
typename std::enable_if<
+ // T being unsigned
! std::is_signed::value,
- unsigned int
+ std::uintmax_t
>::type
constexpr log(T x){
- unsigned i = 0;
+ std::uintmax_t i = 0;
for(; x > 0; ++i)
x >>= 1;
return i;
}
template
typename std::enable_if<
+ // T being signed
std::is_signed::value,
- unsigned int
+ std::uintmax_t
>::type
constexpr log(T x){
if(x < 0)
@@ -41,10 +44,12 @@ namespace numeric {
static_cast::type>(x)
) + 1;
}
+ // return type required to store a particular range
template<
std::intmax_t Min,
std::intmax_t Max
>
+ // signed range
using signed_stored_type = typename boost::int_t<
std::max({log(Min), log(Max)})
>::least ;
@@ -53,11 +58,10 @@ namespace numeric {
std::uintmax_t Min,
std::uintmax_t Max
>
+ // unsigned range
using unsigned_stored_type = typename boost::uint_t<
std::max({log(Min), log(Max)})
>::least ;
-
-
} // numeric
} // boost
diff --git a/test/test_left_shift_automatic.cpp b/test/test_left_shift_automatic.cpp
index 4d3ecc0..7942669 100644
--- a/test/test_left_shift_automatic.cpp
+++ b/test/test_left_shift_automatic.cpp
@@ -42,7 +42,7 @@ const char *test_left_shift_result[VALUE_ARRAY_SIZE] = {
/*10*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
/*11*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
/*12*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
-/*13*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
+/*13*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
/*14*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
/*15*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
@@ -64,8 +64,8 @@ const char *test_left_shift_result[VALUE_ARRAY_SIZE] = {
/*27*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
/*28*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
/*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"
+/*30*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+/*31*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
};
#include
diff --git a/test/test_left_shift_native.cpp b/test/test_left_shift_native.cpp
index 80a3012..59ef1a7 100644
--- a/test/test_left_shift_native.cpp
+++ b/test/test_left_shift_native.cpp
@@ -6,6 +6,7 @@
#include
#include
+#include
#include "../include/safe_integer.hpp"
#include "../include/native.hpp"
@@ -38,11 +39,11 @@ const char *test_left_shift_result[VALUE_ARRAY_SIZE] = {
/* 7*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
/* 8*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
-/* 9*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
+/* 9*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
/*10*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
/*11*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
/*12*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
-/*13*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
+/*13*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
/*14*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
/*15*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
@@ -60,23 +61,24 @@ const char *test_left_shift_result[VALUE_ARRAY_SIZE] = {
/*24*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
/*25*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
-/*26*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
-/*27*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
+/*26*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+/*27*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
/*28*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
/*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"
+/*30*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+/*31*/ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
};
#include
-#define TEST_IMPL(v1, v2, result) \
- rval &= test_left_shift( \
- v1, \
- v2, \
- BOOST_PP_STRINGIZE(v1), \
- BOOST_PP_STRINGIZE(v2), \
- result \
+
+#define TEST_IMPL(v1, v2, result) \
+ rval &= test_left_shift( \
+ v1, \
+ v2, \
+ BOOST_PP_STRINGIZE(v1), \
+ BOOST_PP_STRINGIZE(v2), \
+ result \
);
/**/
diff --git a/test/test_right_shift_automatic.cpp b/test/test_right_shift_automatic.cpp
index 5cbe07f..5e38975 100644
--- a/test/test_right_shift_automatic.cpp
+++ b/test/test_right_shift_automatic.cpp
@@ -49,23 +49,23 @@ const char *test_right_shift_result[VALUE_ARRAY_SIZE] = {
// 0 0 0 0
// 01234567012345670123456701234567
// 01234567890123456789012345678901
-/*16*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
-/*17*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
-/*18*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
-/*19*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
-/*20*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
-/*21*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
-/*22*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
-/*23*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
+/*16*/ "..xx..xx..xx..xx................",
+/*17*/ "..xx..xx..xx..xx................",
+/*18*/ "..xx..xx..xx..xx................",
+/*19*/ "..xx..xx..xx..xx................",
+/*20*/ "..xx..xx..xx..xx................",
+/*21*/ "..xx..xx..xx..xx................",
+/*22*/ "..xx..xx..xx..xx................",
+/*23*/ "..xx..xx..xx..xx................",
-/*24*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
-/*25*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
-/*26*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
-/*27*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
-/*28*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
-/*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"
+/*24*/ "..xx..xx..xx..xx................",
+/*25*/ "..xx..xx..xx..xx................",
+/*26*/ "..xx..xx..xx..xx................",
+/*27*/ "..xx..xx..xx..xx................",
+/*28*/ "..xx..xx..xx..xx................",
+/*29*/ "..xx..xx..xx..xx................",
+/*30*/ "..xx..xx..xx..xx................",
+/*31*/ "..xx..xx..xx..xx................"
};
#include
diff --git a/test/test_right_shift_native.cpp b/test/test_right_shift_native.cpp
index 7536418..8ebbcce 100644
--- a/test/test_right_shift_native.cpp
+++ b/test/test_right_shift_native.cpp
@@ -49,23 +49,23 @@ const char *test_right_shift_result[VALUE_ARRAY_SIZE] = {
// 0 0 0 0
// 01234567012345670123456701234567
// 01234567890123456789012345678901
-/*16*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
-/*17*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
-/*18*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
-/*19*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
-/*20*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
-/*21*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
-/*22*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
-/*23*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
+/*16*/ "..xx..xx..xx..xx................",
+/*17*/ "..xx..xx..xx..xx................",
+/*18*/ "..xx..xx..xx..xx................",
+/*19*/ "..xx..xx..xx..xx................",
+/*20*/ "..xx..xx..xx..xx................",
+/*21*/ "..xx..xx..xx..xx................",
+/*22*/ "..xx..xx..xx..xx................",
+/*23*/ "..xx..xx..xx..xx................",
-/*24*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
-/*25*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
-/*26*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
-/*27*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
-/*28*/ ".xxx.xxx.xxx.xxx.xxx.xxx.xxx.xxx",
-/*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"
+/*24*/ "..xx..xx..xx..xx................",
+/*25*/ "..xx..xx..xx..xx................",
+/*26*/ "..xx..xx..xx..xx................",
+/*27*/ "..xx..xx..xx..xx................",
+/*28*/ "..xx..xx..xx..xx................",
+/*29*/ "..xx..xx..xx..xx................",
+/*30*/ "..xx..xx..xx..xx................",
+/*31*/ "..xx..xx..xx..xx................"
};
#include