ext-boost: Add safe-numerics.

This commit is contained in:
bunnei 2019-12-14 02:33:01 -05:00
parent 1a1e0fc797
commit caf8e19fb1
27 changed files with 7339 additions and 0 deletions

View file

@ -0,0 +1,13 @@
####################
# add include headers to IDE
file(GLOB include_files
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
"*.hpp"
)
add_custom_target(safe_numerics SOURCES ${include_files})
add_subdirectory("concept")
# end headers in IDE
####################

View file

@ -0,0 +1,479 @@
#ifndef BOOST_NUMERIC_AUTOMATIC_HPP
#define BOOST_NUMERIC_AUTOMATIC_HPP
// Copyright (c) 2012 Robert Ramey
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// policy which creates expanded results types designed
// to avoid overflows.
#include <limits>
#include <cstdint> // (u)intmax_t,
#include <type_traits> // conditional
#include <boost/integer.hpp>
#include "safe_common.hpp"
#include "checked_result.hpp"
#include "checked_default.hpp"
#include "checked_integer.hpp"
#include "checked_result_operations.hpp"
#include "interval.hpp"
#include "utility.hpp"
namespace boost {
namespace safe_numerics {
struct automatic {
private:
// 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.
struct defer_stored_signed_lazily {
template<std::intmax_t Min, std::intmax_t Max>
using type = utility::signed_stored_type<Min, Max>;
};
struct defer_stored_unsigned_lazily {
template<std::uintmax_t Min, std::uintmax_t Max>
using type = utility::unsigned_stored_type<Min, Max>;
};
template<typename T, T Min, T Max>
struct result_type {
using type = typename std::conditional<
std::numeric_limits<T>::is_signed,
defer_stored_signed_lazily,
defer_stored_unsigned_lazily
>::type::template type<Min, Max>;
};
public:
///////////////////////////////////////////////////////////////////////
template<typename T, typename U>
struct addition_result {
using temp_base_type = typename std::conditional<
// if both arguments are unsigned
! std::numeric_limits<T>::is_signed
&& ! std::numeric_limits<U>::is_signed,
// result is unsigned
std::uintmax_t,
// otherwise result is signed
std::intmax_t
>::type;
using r_type = checked_result<temp_base_type>;
using r_interval_type = interval<r_type>;
constexpr static const r_interval_type t_interval{
checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::min())),
checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::max()))
};
constexpr static const r_interval_type u_interval{
checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::min())),
checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::max()))
};
constexpr static const r_interval_type r_interval = t_interval + u_interval;
constexpr static auto rl = r_interval.l;
constexpr static auto ru = r_interval.u;
using type = typename result_type<
temp_base_type,
rl.exception()
? std::numeric_limits<temp_base_type>::min()
: static_cast<temp_base_type>(rl),
ru.exception()
? std::numeric_limits<temp_base_type>::max()
: static_cast<temp_base_type>(ru)
>::type;
};
///////////////////////////////////////////////////////////////////////
template<typename T, typename U>
struct subtraction_result {
// result of subtraction are always signed.
using temp_base_type = intmax_t;
using r_type = checked_result<temp_base_type>;
using r_interval_type = interval<r_type>;
constexpr static const r_interval_type t_interval{
checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::min())),
checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::max()))
};
constexpr static const r_interval_type u_interval{
checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::min())),
checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::max()))
};
constexpr static const r_interval_type r_interval = t_interval - u_interval;
constexpr static auto rl = r_interval.l;
constexpr static auto ru = r_interval.u;
using type = typename result_type<
temp_base_type,
rl.exception()
? std::numeric_limits<temp_base_type>::min()
: static_cast<temp_base_type>(rl),
ru.exception()
? std::numeric_limits<temp_base_type>::max()
: static_cast<temp_base_type>(ru)
>::type;
};
///////////////////////////////////////////////////////////////////////
template<typename T, typename U>
struct multiplication_result {
using temp_base_type = typename std::conditional<
// if both arguments are unsigned
! std::numeric_limits<T>::is_signed
&& ! std::numeric_limits<U>::is_signed,
// result is unsigned
std::uintmax_t,
// otherwise result is signed
std::intmax_t
>::type;
using r_type = checked_result<temp_base_type>;
using r_interval_type = interval<r_type>;
constexpr static const r_interval_type t_interval{
checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::min())),
checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::max()))
};
constexpr static const r_interval_type u_interval{
checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::min())),
checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::max()))
};
constexpr static const r_interval_type r_interval = t_interval * u_interval;
constexpr static auto rl = r_interval.l;
constexpr static auto ru = r_interval.u;
using type = typename result_type<
temp_base_type,
rl.exception()
? std::numeric_limits<temp_base_type>::min()
: static_cast<temp_base_type>(rl),
ru.exception()
? std::numeric_limits<temp_base_type>::max()
: static_cast<temp_base_type>(ru)
>::type;
};
///////////////////////////////////////////////////////////////////////
template<typename T, typename U>
struct division_result {
using temp_base_type = typename std::conditional<
// if both arguments are unsigned
! std::numeric_limits<T>::is_signed
&& ! std::numeric_limits<U>::is_signed,
// result is unsigned
std::uintmax_t,
// otherwise result is signed
std::intmax_t
>::type;
using r_type = checked_result<temp_base_type>;
using r_interval_type = interval<r_type>;
constexpr static const r_interval_type t_interval{
checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::min())),
checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::max()))
};
constexpr static const r_interval_type u_interval{
checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::min())),
checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::max()))
};
constexpr static const r_interval_type rx(){
if(u_interval.u < r_type(0)
|| u_interval.l > r_type(0))
return t_interval / u_interval;
return utility::minmax(
std::initializer_list<r_type> {
t_interval.l / u_interval.l,
t_interval.l / r_type(-1),
t_interval.l / r_type(1),
t_interval.l / u_interval.u,
t_interval.u / u_interval.l,
t_interval.u / r_type(-1),
t_interval.u / r_type(1),
t_interval.u / u_interval.u,
}
);
}
constexpr static const r_interval_type r_interval = rx();
constexpr static auto rl = r_interval.l;
constexpr static auto ru = r_interval.u;
using type = typename result_type<
temp_base_type,
rl.exception()
? std::numeric_limits<temp_base_type>::min()
: static_cast<temp_base_type>(rl),
ru.exception()
? std::numeric_limits<temp_base_type>::max()
: static_cast<temp_base_type>(ru)
>::type;
};
///////////////////////////////////////////////////////////////////////
template<typename T, typename U>
struct modulus_result {
using temp_base_type = typename std::conditional<
// if both arguments are unsigned
! std::numeric_limits<T>::is_signed
&& ! std::numeric_limits<U>::is_signed,
// result is unsigned
std::uintmax_t,
// otherwise result is signed
std::intmax_t
>::type;
using r_type = checked_result<temp_base_type>;
using r_interval_type = interval<r_type>;
constexpr static const r_interval_type t_interval{
checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::min())),
checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::max()))
};
constexpr static const r_interval_type u_interval{
checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::min())),
checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::max()))
};
constexpr static const r_interval_type rx(){
if(u_interval.u < r_type(0)
|| u_interval.l > r_type(0))
return t_interval / u_interval;
return utility::minmax(
std::initializer_list<r_type> {
t_interval.l % u_interval.l,
t_interval.l % r_type(-1),
t_interval.l % r_type(1),
t_interval.l % u_interval.u,
t_interval.u % u_interval.l,
t_interval.u % r_type(-1),
t_interval.u % r_type(1),
t_interval.u % u_interval.u,
}
);
}
constexpr static const r_interval_type r_interval = rx();
constexpr static auto rl = r_interval.l;
constexpr static auto ru = r_interval.u;
using type = typename result_type<
temp_base_type,
rl.exception()
? std::numeric_limits<temp_base_type>::min()
: static_cast<temp_base_type>(rl),
ru.exception()
? std::numeric_limits<temp_base_type>::max()
: static_cast<temp_base_type>(ru)
>::type;
};
///////////////////////////////////////////////////////////////////////
// note: comparison_result (<, >, ...) is special.
// The return value is always a bool. The type returned here is
// the intermediate type applied to make the values comparable.
template<typename T, typename U>
struct comparison_result {
using temp_base_type = typename std::conditional<
// if both arguments are unsigned
! std::numeric_limits<T>::is_signed
&& ! std::numeric_limits<U>::is_signed,
// result is unsigned
std::uintmax_t,
// otherwise result is signed
std::intmax_t
>::type;
using r_type = checked_result<temp_base_type>;
using r_interval_type = interval<r_type>;
constexpr static const r_interval_type t_interval{
checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::min())),
checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::max()))
};
constexpr static const r_interval_type u_interval{
checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::min())),
checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::max()))
};
// workaround some microsoft problem
#if 0
constexpr static r_type min(const r_type & t, const r_type & u){
// assert(! u.exception());
// assert(! t.exception());
return static_cast<bool>(t < u) ? t : u;
}
constexpr static r_type max(const r_type & t, const r_type & u){
// assert(! u.exception());
// assert(! t.exception());
return static_cast<bool>(t < u) ? u : t;
}
#endif
// union of two intervals
// note: we can't use t_interval | u_interval because it
// depends on max and min which in turn depend on < which in turn
// depends on implicit conversion of tribool to bool
constexpr static r_interval_type union_interval(
const r_interval_type & t,
const r_interval_type & u
){
//const r_type & rl = min(t.l, u.l);
const r_type & rmin = static_cast<bool>(t.l < u.l) ? t.l : u.l;
//const r_type & ru = max(t.u, u.u);
const r_type & rmax = static_cast<bool>(t.u < u.u) ? u.u : t.u;
return r_interval_type(rmin, rmax);
}
constexpr static const r_interval_type r_interval =
union_interval(t_interval, u_interval);
constexpr static auto rl = r_interval.l;
constexpr static auto ru = r_interval.u;
using type = typename result_type<
temp_base_type,
rl.exception()
? std::numeric_limits<temp_base_type>::min()
: static_cast<temp_base_type>(rl),
ru.exception()
? std::numeric_limits<temp_base_type>::max()
: static_cast<temp_base_type>(ru)
>::type;
};
///////////////////////////////////////////////////////////////////////
// shift operations
template<typename T, typename U>
struct left_shift_result {
using temp_base_type = typename std::conditional<
std::numeric_limits<T>::is_signed,
std::intmax_t,
std::uintmax_t
>::type;
using r_type = checked_result<temp_base_type>;
using r_interval_type = interval<r_type>;
constexpr static const r_interval_type t_interval{
checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::min())),
checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::max()))
};
constexpr static const r_interval_type u_interval{
checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::min())),
checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::max()))
};
constexpr static const r_interval_type r_interval =
t_interval << u_interval;
constexpr static auto rl = r_interval.l;
constexpr static auto ru = r_interval.u;
using type = typename result_type<
temp_base_type,
rl.exception()
? std::numeric_limits<temp_base_type>::min()
: static_cast<temp_base_type>(rl),
ru.exception()
? std::numeric_limits<temp_base_type>::max()
: static_cast<temp_base_type>(ru)
>::type;
};
///////////////////////////////////////////////////////////////////////
template<typename T, typename U>
struct right_shift_result {
using temp_base_type = typename std::conditional<
std::numeric_limits<T>::is_signed,
std::intmax_t,
std::uintmax_t
>::type;
using r_type = checked_result<temp_base_type>;
using r_interval_type = interval<r_type>;
constexpr static const r_interval_type t_interval{
checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::min())),
checked::cast<temp_base_type>(base_value(std::numeric_limits<T>::max()))
};
constexpr static const r_type u_min
= checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::min()));
constexpr static const r_interval_type u_interval{
u_min.exception()
? r_type(0)
: u_min,
checked::cast<temp_base_type>(base_value(std::numeric_limits<U>::max()))
};
constexpr static const r_interval_type r_interval = t_interval >> u_interval;
constexpr static auto rl = r_interval.l;
constexpr static auto ru = r_interval.u;
using type = typename result_type<
temp_base_type,
rl.exception()
? std::numeric_limits<temp_base_type>::min()
: static_cast<temp_base_type>(rl),
ru.exception()
? std::numeric_limits<temp_base_type>::max()
: static_cast<temp_base_type>(ru)
>::type;
};
///////////////////////////////////////////////////////////////////////
template<typename T, typename U>
struct bitwise_and_result {
using type = decltype(
typename base_type<T>::type()
& typename base_type<U>::type()
);
};
template<typename T, typename U>
struct bitwise_or_result {
using type = decltype(
typename base_type<T>::type()
| typename base_type<U>::type()
);
};
template<typename T, typename U>
struct bitwise_xor_result {
using type = decltype(
typename base_type<T>::type()
^ typename base_type<U>::type()
);
};
};
} // safe_numerics
} // boost
#endif // BOOST_NUMERIC_AUTOMATIC_HPP

View file

@ -0,0 +1,217 @@
#ifndef BOOST_NUMERIC_CHECKED_DEFAULT_HPP
#define BOOST_NUMERIC_CHECKED_DEFAULT_HPP
// Copyright (c) 2017 Robert Ramey
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// contains operation implementation of arithmetic operators
// on built-in types. The default implementation is to just
// invoke the operation with no checking. These are overloaded
// for specific types such as integer, etc.
// implement the equivant of template partial specialization for functions
// what we need is
// a) a default implementation of add, subtract, etc which just
// implements the standard operations and returns the result
// b) specific implementations to be called from safe implementation
// such as safe<int> ... and someday maybe money<T, D> ...
//
// What we need is partial function specialization - but this doesn't
// exist in C++ (yet?). But particial specialization of structures DOES
// exist. So put our functions into a class which can then be
// partially specialized. Finally. add a function interface to so that
// data types can be deduced from the function call. We now have
// the equivalent of partial function template specialization.
// usage example: checked<int>::add(t, u) ...
#include <boost/logic/tribool.hpp>
#include "checked_result.hpp"
namespace boost {
namespace safe_numerics {
// main function object which contains functions which handle
// primitives which haven't been overriden. For now, these
// implement the default operation. But I see this as an indicator
// that there is more work to be done. For example float * int should
// never be called because promotions on operands should occur before
// the operation is invoked. So rather than returning the default operation
// it should trap with a static_assert. This occurs at compile time while
// calculating result interval. This needs more investigation.
template<
typename R,
R Min,
R Max,
typename T,
class F = make_checked_result<R>,
class Default = void
>
struct heterogeneous_checked_operation {
constexpr static checked_result<R>
cast(const T & t) /* noexcept */ {
return static_cast<R>(t);
}
};
template<
typename R,
class F = make_checked_result<R>,
class Default = void
>
struct checked_operation{
constexpr static checked_result<R>
minus(const R & t) noexcept {
return - t;
}
constexpr static checked_result<R>
add(const R & t, const R & u) noexcept {
return t + u;
}
constexpr static checked_result<R>
subtract(const R & t, const R & u) noexcept {
return t - u;
}
constexpr static checked_result<R>
multiply(const R & t, const R & u) noexcept {
return t * u;
}
constexpr static checked_result<R>
divide(const R & t, const R & u) noexcept {
return t / u;
}
constexpr static checked_result<R>
modulus(const R & t, const R & u) noexcept {
return t % u;
}
constexpr static boost::logic::tribool
less_than(const R & t, const R & u) noexcept {
return t < u;
}
constexpr static boost::logic::tribool
greater_than(const R & t, const R & u) noexcept {
return t > u;
}
constexpr static boost::logic::tribool
equal(const R & t, const R & u) noexcept {
return t < u;
}
constexpr static checked_result<R>
left_shift(const R & t, const R & u) noexcept {
return t << u;
}
constexpr static checked_result<R>
right_shift(const R & t, const R & u) noexcept {
return t >> u;
}
constexpr static checked_result<R>
bitwise_or(const R & t, const R & u) noexcept {
return t | u;
}
constexpr static checked_result<R>
bitwise_xor(const R & t, const R & u) noexcept {
return t ^ u;
}
constexpr static checked_result<R>
bitwise_and(const R & t, const R & u) noexcept {
return t & u;
}
constexpr static checked_result<R>
bitwise_not(const R & t) noexcept {
return ~t;
}
};
namespace checked {
// implement function call interface so that types other than
// the result type R can be deduced from the function parameters.
template<typename R, typename T>
constexpr checked_result<R> cast(const T & t) /* noexcept */ {
return heterogeneous_checked_operation<
R,
std::numeric_limits<R>::min(),
std::numeric_limits<R>::max(),
T
>::cast(t);
}
template<typename R>
constexpr checked_result<R> minus(const R & t) noexcept {
return checked_operation<R>::minus(t);
}
template<typename R>
constexpr checked_result<R> add(const R & t, const R & u) noexcept {
return checked_operation<R>::add(t, u);
}
template<typename R>
constexpr checked_result<R> subtract(const R & t, const R & u) noexcept {
return checked_operation<R>::subtract(t, u);
}
template<typename R>
constexpr checked_result<R> multiply(const R & t, const R & u) noexcept {
return checked_operation<R>::multiply(t, u);
}
template<typename R>
constexpr checked_result<R> divide(const R & t, const R & u) noexcept {
return checked_operation<R>::divide(t, u);
}
template<typename R>
constexpr checked_result<R> modulus(const R & t, const R & u) noexcept {
return checked_operation<R>::modulus(t, u);
}
template<typename R>
constexpr checked_result<bool> less_than(const R & t, const R & u) noexcept {
return checked_operation<R>::less_than(t, u);
}
template<typename R>
constexpr checked_result<bool> greater_than_equal(const R & t, const R & u) noexcept {
return ! checked_operation<R>::less_than(t, u);
}
template<typename R>
constexpr checked_result<bool> greater_than(const R & t, const R & u) noexcept {
return checked_operation<R>::greater_than(t, u);
}
template<typename R>
constexpr checked_result<bool> less_than_equal(const R & t, const R & u) noexcept {
return ! checked_operation<R>::greater_than(t, u);
}
template<typename R>
constexpr checked_result<bool> equal(const R & t, const R & u) noexcept {
return checked_operation<R>::equal(t, u);
}
template<typename R>
constexpr checked_result<R> left_shift(const R & t, const R & u) noexcept {
return checked_operation<R>::left_shift(t, u);
}
template<typename R>
constexpr checked_result<R> right_shift(const R & t, const R & u) noexcept {
return checked_operation<R>::right_shift(t, u);
}
template<typename R>
constexpr checked_result<R> bitwise_or(const R & t, const R & u) noexcept {
return checked_operation<R>::bitwise_or(t, u);
}
template<typename R>
constexpr checked_result<R> bitwise_xor(const R & t, const R & u) noexcept {
return checked_operation<R>::bitwise_xor(t, u);
}
template<typename R>
constexpr checked_result<R> bitwise_and(const R & t, const R & u) noexcept {
return checked_operation<R>::bitwise_and(t, u);
}
template<typename R>
constexpr checked_result<R> bitwise_not(const R & t) noexcept {
return checked_operation<R>::bitwise_not(t);
}
} // checked
} // safe_numerics
} // boost
#endif // BOOST_NUMERIC_CHECKED_DEFAULT_HPP

View file

@ -0,0 +1,214 @@
#ifndef BOOST_NUMERIC_CHECKED_FLOAT_HPP
#define BOOST_NUMERIC_CHECKED_FLOAT_HPP
// Copyright (c) 2017 Robert Ramey
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// contains operation implementation of arithmetic operators
// on built-in floating point types. The default implementation is to just
// invoke the operation with no checking. These are overloaded
// for specific types such as integer, etc.
#include <type_traits> // std::is_floating_point, make_unsigned
namespace boost {
namespace safe_numerics {
namespace checked {
////////////////////////////////////////////////////
// layer 0 - implement safe operations for floating
template<
typename R,
R Min,
R Max,
typename T,
class F
>
struct heterogeneous_checked_operation<
R,
Min,
Max,
T,
F,
typename std::enable_if<
std::is_floating_point<R>::value
&& std::is_floating_point<T>::value
>::type
>{
constexpr static checked_result<R>
cast(const T & t) noexcept {
return t;
};
}; // checked_unary_operation
template<
typename R,
R Min,
R Max,
typename T,
class
F
>
struct heterogeneous_checked_operation<
R,
Min,
Max,
T,
F,
typename std::enable_if<
std::is_floating_point<R>::value
&& std::is_integralt<T>::value
>::type
>{
constexpr static checked_result<R>
cast(const T & t) noexcept {
return t;
};
}; // checked_unary_operation
template<typename R, typename T, typename U>
struct checked_operation<R, T, U, F,
typename std::enable_if<
std::is_floating_point<R>::value
>::type
>{
constexpr static checked_result<R> cast(const T & t) {
return
cast_impl_detail::cast_impl(
t,
std::is_signed<R>(),
std::is_signed<T>()
);
}
constexpr static checked_result<R> add(const T & t, const U & u) {
return t + u;
}
constexpr static checked_result<R> subtract(
const T & t,
const U & u
) {
return t - u;
}
constexpr static checked_result<R> multiply(
const T & t,
const U & u
) noexcept {
return t * u;
}
constexpr static checked_result<R> divide(
const T & t,
const U & u
) noexcept {
return t / u;
}
constexpr static checked_result<R> modulus(
const T & t,
const U & u
) noexcept {
return t % u;
}
constexpr static bool less_than(const T & t, const U & u) noexcept {
return t < u;
}
constexpr static bool greater_than(const T & t, const U & u) noexcept {
return t > u;
}
constexpr static bool equal(const T & t, const U & u) noexcept {
return t < u;
}
}; // checked_binary_operation
template<class R, class T, class U>
typename std::enable_if<
std::is_floating_point<R>::value
&& std::is_floating_point<T>::value
&& std::is_floating_point<U>::value,
checked_result<R>
>::type
constexpr bool less_than(const T & t, const U & u) noexcept {
return t < u;
}
template<class R, class T, class U>
typename std::enable_if<
std::is_floating_point<R>::value
&& std::is_floating_point<T>::value
&& std::is_floating_point<U>::value,
checked_result<R>
>::type
constexpr bool equal(const T & t, const U & u) noexcept {
return t < u;
}
template<class R, class T, class U>
typename std::enable_if<
std::is_floating_point<R>::value
&& std::is_floating_point<T>::value
&& std::is_floating_point<U>::value,
checked_result<R>
>::type
constexpr checked_result<R> left_shift(const T & t, const U & u) noexcept {
return t << u;
}
template<class R, class T, class U>
typename std::enable_if<
std::is_floating_point<R>::value
&& std::is_floating_point<T>::value
&& std::is_floating_point<U>::value,
checked_result<R>
>::type
constexpr checked_result<R> right_shift(const T & t, const U & u) noexcept {
return t >> u;
}
template<class R, class T, class U>
typename std::enable_if<
std::is_floating_point<R>::value
&& std::is_floating_point<T>::value
&& std::is_floating_point<U>::value,
checked_result<R>
>::type
constexpr checked_result<R> bitwise_or(const T & t, const U & u) noexcept {
return t | u;
}
template<class R, class T, class U>
typename std::enable_if<
std::is_floating_point<R>::value
&& std::is_floating_point<T>::value
&& std::is_floating_point<U>::value,
checked_result<R>
>::type
constexpr checked_result<R> bitwise_xor(const T & t, const U & u) noexcept {
return t ^ u;
}
template<class R, class T, class U>
typename std::enable_if<
std::is_floating_point<R>::value
&& std::is_floating_point<T>::value
&& std::is_floating_point<U>::value,
checked_result<R>
>::type
constexpr checked_result<R> bitwise_and(const T & t, const U & u) noexcept {
return t & u;
}
} // checked
} // safe_numerics
} // boost
#endif // BOOST_NUMERIC_CHECKED_DEFAULT_HPP

View file

@ -0,0 +1,819 @@
#ifndef BOOST_NUMERIC_CHECKED_INTEGER_HPP
#define BOOST_NUMERIC_CHECKED_INTEGER_HPP
// Copyright (c) 2012 Robert Ramey
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// contains operations for doing checked aritmetic on NATIVE
// C++ types.
#include <limits>
#include <type_traits> // is_integral, make_unsigned, enable_if
#include <algorithm> // std::max
#include "checked_result.hpp"
#include "checked_default.hpp"
#include "safe_compare.hpp"
#include "utility.hpp"
#include "exception.hpp"
namespace boost {
namespace safe_numerics {
// utility
template<bool tf>
using bool_type = typename std::conditional<tf, std::true_type, std::false_type>::type;
////////////////////////////////////////////////////
// layer 0 - implement safe operations for intrinsic integers
// Note presumption of twos complement integer arithmetic
// convert an integral value to some other integral type
template<
typename R,
R Min,
R Max,
typename T,
class F
>
struct heterogeneous_checked_operation<
R,
Min,
Max,
T,
F,
typename std::enable_if<
std::is_integral<R>::value
&& std::is_integral<T>::value
>::type
>{
////////////////////////////////////////////////////
// safe casting on primitive types
struct cast_impl_detail {
constexpr static checked_result<R>
cast_impl(
const T & t,
std::true_type, // R is signed
std::true_type // T is signed
){
// INT32-C Ensure that operations on signed
// integers do not overflow
return
boost::safe_numerics::safe_compare::greater_than(
t,
Max
) ?
F::template invoke<safe_numerics_error::positive_overflow_error>(
"converted signed value too large"
)
:
boost::safe_numerics::safe_compare::less_than(
t,
Min
) ?
F::template invoke<safe_numerics_error::negative_overflow_error>(
"converted signed value too small"
)
:
checked_result<R>(static_cast<R>(t))
;
}
constexpr static checked_result<R>
cast_impl(
const T & t,
std::true_type, // R is signed
std::false_type // T is unsigned
){
// INT30-C Ensure that unsigned integer operations
// do not wrap
return
boost::safe_numerics::safe_compare::greater_than(
t,
Max
) ?
F::template invoke<safe_numerics_error::positive_overflow_error>(
"converted unsigned value too large"
)
:
checked_result<R>(static_cast<R>(t))
;
}
constexpr static checked_result<R>
cast_impl(
const T & t,
std::false_type, // R is unsigned
std::false_type // T is unsigned
){
// INT32-C Ensure that operations on unsigned
// integers do not overflow
return
boost::safe_numerics::safe_compare::greater_than(
t,
Max
) ?
F::template invoke<safe_numerics_error::positive_overflow_error>(
"converted unsigned value too large"
)
:
checked_result<R>(static_cast<R>(t))
;
}
constexpr static checked_result<R>
cast_impl(
const T & t,
std::false_type, // R is unsigned
std::true_type // T is signed
){
return
boost::safe_numerics::safe_compare::less_than(t, 0) ?
F::template invoke<safe_numerics_error::domain_error>(
"converted negative value to unsigned"
)
:
boost::safe_numerics::safe_compare::greater_than(
t,
Max
) ?
F::template invoke<safe_numerics_error::positive_overflow_error>(
"converted signed value too large"
)
:
checked_result<R>(static_cast<R>(t))
;
}
}; // cast_impl_detail
constexpr static checked_result<R>
cast(const T & t){
return
cast_impl_detail::cast_impl(
t,
std::is_signed<R>(),
std::is_signed<T>()
);
}
};
// converting floating point value to integral type
template<
typename R,
R Min,
R Max,
typename T,
class F
>
struct heterogeneous_checked_operation<
R,
Min,
Max,
T,
F,
typename std::enable_if<
std::is_integral<R>::value
&& std::is_floating_point<T>::value
>::type
>{
constexpr static checked_result<R>
cast(const T & t){
return static_cast<R>(t);
}
};
// converting integral value to floating point type
// INT35-C. Use correct integer precisions
template<
typename R,
R Min,
R Max,
typename T,
class F
>
struct heterogeneous_checked_operation<
R,
Min,
Max,
T,
F,
typename std::enable_if<
std::is_floating_point<R>::value
&& std::is_integral<T>::value
>::type
>{
constexpr static checked_result<R>
cast(const T & t){
if(std::numeric_limits<R>::digits < std::numeric_limits<T>::digits){
if(utility::significant_bits(t) > std::numeric_limits<R>::digits){
return F::invoke(
safe_numerics_error::precision_overflow_error,
"keep precision"
);
}
}
return t;
}
};
// binary operations on primitive integer types
template<
typename R,
class F
>
struct checked_operation<R, F,
typename std::enable_if<
std::is_integral<R>::value
>::type
>{
////////////////////////////////////////////////////
// safe addition on primitive types
struct add_impl_detail {
// result unsigned
constexpr static checked_result<R> add(
const R t,
const R u,
std::false_type // R unsigned
){
return
// INT30-C. Ensure that unsigned integer operations do not wrap
std::numeric_limits<R>::max() - u < t ?
F::template invoke<safe_numerics_error::positive_overflow_error>(
"addition result too large"
)
:
checked_result<R>(t + u)
;
}
// result signed
constexpr static checked_result<R> add(
const R t,
const R u,
std::true_type // R signed
){
// INT32-C. Ensure that operations on signed integers do not result in overflow
return
// INT32-C. Ensure that operations on signed integers do not result in overflow
((u > 0) && (t > (std::numeric_limits<R>::max() - u))) ?
F::template invoke<safe_numerics_error::positive_overflow_error>(
"addition result too large"
)
:
((u < 0) && (t < (std::numeric_limits<R>::min() - u))) ?
F::template invoke<safe_numerics_error::negative_overflow_error>(
"addition result too low"
)
:
checked_result<R>(t + u)
;
}
}; // add_impl_detail
constexpr static checked_result<R>
add(const R & t, const R & u){
return add_impl_detail::add(t, u, std::is_signed<R>());
}
////////////////////////////////////////////////////
// safe subtraction on primitive types
struct subtract_impl_detail {
// result unsigned
constexpr static checked_result<R> subtract(
const R t,
const R u,
std::false_type // R is unsigned
){
// INT30-C. Ensure that unsigned integer operations do not wrap
return
t < u ?
F::template invoke<safe_numerics_error::negative_overflow_error>(
"subtraction result cannot be negative"
)
:
checked_result<R>(t - u)
;
}
// result signed
constexpr static checked_result<R> subtract(
const R t,
const R u,
std::true_type // R is signed
){ // INT32-C
return
// INT32-C. Ensure that operations on signed integers do not result in overflow
((u > 0) && (t < (std::numeric_limits<R>::min() + u))) ?
F::template invoke<safe_numerics_error::negative_overflow_error>(
"subtraction result overflows result type"
)
:
((u < 0) && (t > (std::numeric_limits<R>::max() + u))) ?
F::template invoke<safe_numerics_error::positive_overflow_error>(
"subtraction result overflows result type"
)
:
checked_result<R>(t - u)
;
}
}; // subtract_impl_detail
constexpr static checked_result<R> subtract(const R & t, const R & u){
return subtract_impl_detail::subtract(t, u, std::is_signed<R>());
}
////////////////////////////////////////////////////
// safe minus on primitive types
struct minus_impl_detail {
// result unsigned
constexpr static checked_result<R> minus(
const R t,
std::false_type // R is unsigned
){
return t > 0 ?
F::template invoke<safe_numerics_error::negative_overflow_error>(
"minus unsigned would be negative"
)
:
// t == 0
checked_result<R>(0)
;
}
// result signed
constexpr static checked_result<R> minus(
const R t,
std::true_type // R is signed
){ // INT32-C
return t == std::numeric_limits<R>::min() ?
F::template invoke<safe_numerics_error::positive_overflow_error>(
"subtraction result overflows result type"
)
:
checked_result<R>(-t)
;
}
}; // minus_impl_detail
constexpr static checked_result<R> minus(const R & t){
return minus_impl_detail::minus(t, std::is_signed<R>());
}
////////////////////////////////////////////////////
// safe multiplication on primitive types
struct multiply_impl_detail {
// result unsigned
constexpr static checked_result<R> multiply(
const R t,
const R u,
std::false_type, // R is unsigned
std::false_type // !(sizeochecked_result<R>R) > sizeochecked_result<R>std::uintmax_t) / 2)
){
// INT30-C
// fast method using intermediate result guaranteed not to overflow
// todo - replace std::uintmax_t with a size double the size of R
using i_type = std::uintmax_t;
return
static_cast<i_type>(t) * static_cast<i_type>(u)
> std::numeric_limits<R>::max() ?
F::template invoke<safe_numerics_error::positive_overflow_error>(
"multiplication overflow"
)
:
checked_result<R>(t * u)
;
}
constexpr static checked_result<R> multiply(
const R t,
const R u,
std::false_type, // R is unsigned
std::true_type // (sizeochecked_result<R>R) > sizeochecked_result<R>std::uintmax_t) / 2)
){
// INT30-C
return
u > 0 && t > std::numeric_limits<R>::max() / u ?
F::template invoke<safe_numerics_error::positive_overflow_error>(
"multiplication overflow"
)
:
checked_result<R>(t * u)
;
}
// result signed
constexpr static checked_result<R> multiply(
const R t,
const R u,
std::true_type, // R is signed
std::false_type // ! (sizeochecked_result<R>R) > (sizeochecked_result<R>std::intmax_t) / 2))
){
// INT30-C
// fast method using intermediate result guaranteed not to overflow
// todo - replace std::intmax_t with a size double the size of R
using i_type = std::intmax_t;
return
(
static_cast<i_type>(t) * static_cast<i_type>(u)
> static_cast<i_type>(std::numeric_limits<R>::max())
) ?
F::template invoke<safe_numerics_error::positive_overflow_error>(
"multiplication overflow"
)
:
(
static_cast<i_type>(t) * static_cast<i_type>(u)
< static_cast<i_type>(std::numeric_limits<R>::min())
) ?
F::template invoke<safe_numerics_error::negative_overflow_error>(
"multiplication overflow"
)
:
checked_result<R>(t * u)
;
}
constexpr static checked_result<R> multiply(
const R t,
const R u,
std::true_type, // R is signed
std::true_type // (sizeochecked_result<R>R) > (sizeochecked_result<R>std::intmax_t) / 2))
){ // INT32-C
return t > 0 ?
u > 0 ?
t > std::numeric_limits<R>::max() / u ?
F::template invoke<safe_numerics_error::positive_overflow_error>(
"multiplication overflow"
)
:
checked_result<R>(t * u)
: // u <= 0
u < std::numeric_limits<R>::min() / t ?
F::template invoke<safe_numerics_error::negative_overflow_error>(
"multiplication overflow"
)
:
checked_result<R>(t * u)
: // t <= 0
u > 0 ?
t < std::numeric_limits<R>::min() / u ?
F::template invoke<safe_numerics_error::negative_overflow_error>(
"multiplication overflow"
)
:
checked_result<R>(t * u)
: // u <= 0
t != 0 && u < std::numeric_limits<R>::max() / t ?
F::template invoke<safe_numerics_error::positive_overflow_error>(
"multiplication overflow"
)
:
checked_result<R>(t * u)
;
}
}; // multiply_impl_detail
constexpr static checked_result<R> multiply(const R & t, const R & u){
return multiply_impl_detail::multiply(
t,
u,
std::is_signed<R>(),
std::integral_constant<
bool,
(sizeof(R) > sizeof(std::uintmax_t) / 2)
>()
);
}
////////////////////////////////
// safe division on unsafe types
struct divide_impl_detail {
constexpr static checked_result<R> divide(
const R & t,
const R & u,
std::false_type // R is unsigned
){
return t / u;
}
constexpr static checked_result<R> divide(
const R & t,
const R & u,
std::true_type // R is signed
){
return
(u == -1 && t == std::numeric_limits<R>::min()) ?
F::template invoke<safe_numerics_error::positive_overflow_error>(
"result cannot be represented"
)
:
checked_result<R>(t / u)
;
}
}; // divide_impl_detail
// note that we presume that the size of R >= size of T
constexpr static checked_result<R> divide(const R & t, const R & u){
if(u == 0){
return F::template invoke<safe_numerics_error::domain_error>(
"divide by zero"
);
}
return divide_impl_detail::divide(t, u, std::is_signed<R>());
}
////////////////////////////////
// safe modulus on unsafe types
struct modulus_impl_detail {
constexpr static checked_result<R> modulus(
const R & t,
const R & u,
std::false_type // R is unsigned
){
return t % u;
}
constexpr static checked_result<R> modulus(
const R & t,
const R & u,
std::true_type // R is signed
){
if(u >= 0)
return t % u;
checked_result<R> ux = checked::minus(u);
if(ux.exception())
return t;
return t % static_cast<R>(ux);
}
}; // modulus_impl_detail
constexpr static checked_result<R> modulus(const R & t, const R & u){
if(0 == u)
return F::template invoke<safe_numerics_error::domain_error>(
"denominator is zero"
);
// why to we need abs here? the sign of the modulus is the sign of the
// dividend. 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 modulus_impl_detail::modulus(t, u, typename std::is_signed<R>::type());
}
///////////////////////////////////
// shift operations
struct left_shift_integer_detail {
#if 0
// todo - optimize for gcc to exploit builtin
/* for gcc compilers
int __builtin_clz (unsigned int x)
Returns the number of leading 0-bits in x, starting at the
most significant bit position. If x is 0, the result is undefined.
*/
#ifndef __has_feature // Optional of course.
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
#endif
template<typename T>
constexpr unsigned int leading_zeros(const T & t){
if(0 == t)
return 0;
#if __has_feature(builtin_clz)
return __builtin_clz(t);
#else
#endif
}
#endif
// 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.
constexpr static checked_result<R> left_shift(
const R & t,
const R & u,
std::false_type // R is unsigned
){
// the value of the result is E1 x 2^E2, reduced modulo one more than
// the maximum value representable in the result type.
// see 5.8 & 1
// if right operand is
// greater than or equal to the length in bits of the promoted left operand.
if(
safe_compare::greater_than(
u,
std::numeric_limits<R>::digits - utility::significant_bits(t)
)
){
// behavior is undefined
return F::template invoke<safe_numerics_error::shift_too_large>(
"shifting left more bits than available is undefined behavior"
);
}
return t << u;
}
constexpr static checked_result<R> left_shift(
const R & t,
const R & u,
std::true_type // R is signed
){
// and [E1] has a non-negative value
if(t >= 0){
// and E1 x 2^E2 is representable in the corresponding
// unsigned type of the result type,
// see 5.8 & 1
// if right operand is
// greater than or equal to the length in bits of the promoted left operand.
if(
safe_compare::greater_than(
u,
std::numeric_limits<R>::digits - utility::significant_bits(t)
)
){
// behavior is undefined
return F::template invoke<safe_numerics_error::shift_too_large>(
"shifting left more bits than available"
);
}
else{
return t << u;
}
}
// otherwise, the behavior is undefined.
return F::template invoke<safe_numerics_error::negative_shift>(
"shifting a negative value"
);
}
}; // left_shift_integer_detail
constexpr static checked_result<R> left_shift(
const R & t,
const R & u
){
// INT34-C - Do not shift an expression by a negative number of bits
// standard paragraph 5.8 & 1
// if the right operand is negative
if(u == 0){
return t;
}
if(u < 0){
return F::template invoke<safe_numerics_error::negative_shift>(
"shifting negative amount"
);
}
if(u > std::numeric_limits<R>::digits){
// behavior is undefined
return F::template invoke<safe_numerics_error::shift_too_large>(
"shifting more bits than available"
);
}
return left_shift_integer_detail::left_shift(t, u, std::is_signed<R>());
}
// right shift
struct right_shift_integer_detail {
// INT34-C C++
// standard paragraph 5.8 / 3
// The value of E1 << E2 is E1 left-shifted E2 bit positions;
// vacated bits are zero-filled.
constexpr static checked_result<R> right_shift(
const R & t,
const R & u,
std::false_type // T is unsigned
){
// the value of the result is the integral part of the
// quotient of E1/2E2
return t >> u;
}
constexpr static checked_result<R> right_shift(
const R & t,
const R & u,
std::true_type // T is signed;
){
if(t < 0){
// note that the C++ standard considers this case is "implemenation
// defined" rather than "undefined".
return F::template invoke<safe_numerics_error::negative_value_shift>(
"shifting a negative value"
);
}
// the value is the integral part of E1 / 2^E2,
return t >> u;
}
}; // right_shift_integer_detail
constexpr static checked_result<R> right_shift(
const R & t,
const R & u
){
// INT34-C - Do not shift an expression by a negative number of bits
// standard paragraph 5.8 & 1
// if the right operand is negative
if(u < 0){
return F::template invoke<safe_numerics_error::negative_shift>(
"shifting negative amount"
);
}
if(u > std::numeric_limits<R>::digits){
// behavior is undefined
return F::template invoke<safe_numerics_error::shift_too_large>(
"shifting more bits than available"
);
}
return right_shift_integer_detail::right_shift(t, u ,std::is_signed<R>());
}
///////////////////////////////////
// bitwise operations
// INT13-C Note: We don't enforce recommendation as acually written
// as it would break too many programs. Specifically, we permit signed
// integer operands.
constexpr static checked_result<R> bitwise_or(const R & t, const R & u){
using namespace boost::safe_numerics::utility;
const unsigned int result_size
= std::max(significant_bits(t), significant_bits(u));
if(result_size > bits_type<R>::value){
return F::template invoke<safe_numerics_error::positive_overflow_error>(
"result type too small to hold bitwise or"
);
}
return t | u;
}
constexpr static checked_result<R> bitwise_xor(const R & t, const R & u){
using namespace boost::safe_numerics::utility;
const unsigned int result_size
= std::max(significant_bits(t), significant_bits(u));
if(result_size > bits_type<R>::value){
return F::template invoke<safe_numerics_error::positive_overflow_error>(
"result type too small to hold bitwise or"
);
}
return t ^ u;
}
constexpr static checked_result<R> bitwise_and(const R & t, const R & u){
using namespace boost::safe_numerics::utility;
const unsigned int result_size
= std::min(significant_bits(t), significant_bits(u));
if(result_size > bits_type<R>::value){
return F::template invoke<safe_numerics_error::positive_overflow_error>(
"result type too small to hold bitwise and"
);
}
return t & u;
}
constexpr static checked_result<R> bitwise_not(const R & t){
using namespace boost::safe_numerics::utility;
if(significant_bits(t) > bits_type<R>::value){
return F::template invoke<safe_numerics_error::positive_overflow_error>(
"result type too small to hold bitwise inverse"
);
}
return ~t;
}
}; // checked_operation
} // safe_numerics
} // boost
#endif // BOOST_NUMERIC_CHECKED_INTEGER_HPP

View file

@ -0,0 +1,146 @@
#ifndef BOOST_NUMERIC_CHECKED_RESULT
#define BOOST_NUMERIC_CHECKED_RESULT
// Copyright (c) 2012 Robert Ramey
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// contains operations for doing checked aritmetic on NATIVE
// C++ types.
#include <cassert>
#include <type_traits> // is_convertible
#include "exception.hpp"
namespace boost {
namespace safe_numerics {
template<typename R>
struct checked_result {
const safe_numerics_error m_e;
const union {
R m_r;
char const * m_msg;
};
// don't permit construction without initial value;
checked_result() = delete;
// note: I implemented the following non-default copy and move
// constructors because I thought I needed to do this in order
// to make them constexpr. Turns out though that doing this creates
// a syntax error because the assignment results in error due
// to assignment "outside of object lifetime". I think this could
// be addressed by replacing the anonymous union above with a
// named union. This would create some syntax changes which would
// ripple through some parts of th program. So for now, we'll just
// rely on the default copy and move constructors.
#if 0
// copy constructor
constexpr /*explicit*/ checked_result(const checked_result & r) noexpect :
m_e(r.m_e)
{
if(safe_numerics_error::success == r.m_e)
m_r = r.m_r;
else
m_msg = r.m_msg;
}
// move constructor
constexpr /*explicit*/ checked_result(checked_result && r) noexcept :
m_e(r.m_e)
{
if(safe_numerics_error::success == r.m_e)
m_r = r.m_r;
else
m_msg = r.m_msg;
}
#endif
checked_result(const checked_result & r) = default;
checked_result(checked_result && r) = default;
constexpr /*explicit*/ checked_result(const R & r) :
m_e(safe_numerics_error::success),
m_r(r)
{}
#if 0
template<typename T>
constexpr /*explicit*/ checked_result(const T & t) noexcept :
m_e(safe_numerics_error::success),
m_r(t)
{}
#endif
constexpr /*explicit*/ checked_result(
const safe_numerics_error & e,
const char * msg = ""
) noexcept :
m_e(e),
m_msg(msg)
{
assert(m_e != safe_numerics_error::success);
}
// permit construct from another checked result type
template<typename T>
constexpr /*explicit*/ checked_result(const checked_result<T> & t) noexcept :
m_e(t.m_e)
{
static_assert(
std::is_convertible<T, R>::value,
"T must be convertible to R"
);
if(safe_numerics_error::success == t.m_e)
m_r = t.m_r;
else
m_msg = t.m_msg;
}
constexpr bool exception() const {
return m_e != safe_numerics_error::success;
}
// accesors
constexpr operator R() const noexcept{
// don't assert here. Let the library catch these errors
assert(! exception());
return m_r;
}
constexpr operator safe_numerics_error () const noexcept{
// note that this is a legitimate operation even when
// the operation was successful - it will return success
return m_e;
}
constexpr operator const char *() const noexcept{
assert(exception());
return m_msg;
}
// disallow assignment
checked_result & operator=(const checked_result &) = delete;
};
#if 0
template<typename R>
constexpr checked_result<R> make_checked_result(
const safe_numerics_error & e,
char const * const & m
) noexcept {
return checked_result<R>(e, m);
}
#endif
template <class R>
class make_checked_result {
public:
template<safe_numerics_error E>
constexpr static checked_result<R> invoke(
char const * const & m
) noexcept {
return checked_result<R>(E, m);
}
};
} // safe_numerics
} // boost
#endif // BOOST_NUMERIC_CHECKED_RESULT

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,14 @@
####################
# add include headers to IDE
set(USE_FOLDERS TRUE)
file(GLOB include_files
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}"
"${CMAKE_CURRENT_SOURCE_DIR}/*.hpp"
)
add_custom_target(concepts SOURCES ${include_files})
set_target_properties(concepts PROPERTIES FOLDER "safe_numerics")
# end headers in IDE
####################

View file

@ -0,0 +1,29 @@
#ifndef BOOST_NUMERIC_CONCEPT_EXCEPTION_POLICY_HPP
#define BOOST_NUMERIC_CONCEPT_EXCEPTION_POLICY_HPP
// Copyright (c) 2015 Robert Ramey
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
namespace boost {
namespace safe_numerics {
template<class EP>
struct ExceptionPolicy {
const char * message;
/*
BOOST_CONCEPT_USAGE(ExceptionPolicy){
EP::on_arithmetic_error(e, message);
EP::on_undefined_behavior(e, message)
EP::on_implementation_defined_behavior(e, message)
EP::on_uninitialized_value(e, message)
}
*/
};
} // safe_numerics
} // boost
#endif // BOOST_NUMERIC_CONCEPT_EXCEPTION_POLICY_HPP

View file

@ -0,0 +1,27 @@
#ifndef BOOST_NUMERIC_CONCEPT_INTEGER_HPP
#define BOOST_NUMERIC_CONCEPT_INTEGER_HPP
// Copyright (c) 2012 Robert Ramey
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include "numeric.hpp"
namespace boost {
namespace safe_numerics {
template <class T>
class Integer : public Numeric<T> {
// integer types must have the corresponding numeric trait.
static_assert(
std::numeric_limits<T>::is_integer,
"Fails to fulfill requirements for an integer type"
);
};
} // safe_numerics
} // boost
#endif // BOOST_NUMERIC_CONCEPT_INTEGER_HPP

View file

@ -0,0 +1,29 @@
#ifndef BOOST_NUMERIC_CONCEPT_NUMERIC_HPP
#define BOOST_NUMERIC_CONCEPT_NUMERIC_HPP
// Copyright (c) 2012 Robert Ramey
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <limits>
namespace boost {
namespace safe_numerics {
template<class T>
struct Numeric {
// if your program traps here, you need to create a
// std::numeric_limits class for your type T. see
// see C++ standard 18.3.2.2
static_assert(
std::numeric_limits<T>::is_specialized,
"std::numeric_limits<T> has not been specialized for this type"
);
};
} // safe_numerics
} // boost
#endif // BOOST_NUMERIC_CONCEPT_NUMERIC_HPP

View file

@ -0,0 +1,33 @@
#ifndef BOOST_NUMERIC_CONCEPT_PROMOTION_POLICY_HPP
#define BOOST_NUMERIC_CONCEPT_PROMOTION_POLICY_HPP
// Copyright (c) 2015 Robert Ramey
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
namespace boost {
namespace safe_numerics {
template<class PP>
struct PromotionPolicy {
using T = int;
using U = int;
using a_type = typename PP::template addition_result<T, U>;
using s_type = typename PP::template subtraction_result<T, U>;
using m_type = typename PP::template multiplication_result<T, U>;
using d_type = typename PP::template division_result<T, U>;
using mod_type = typename PP::template modulus_result<T, U>;
using ls_type = typename PP::template left_shift_result<T, U>;
using rs_type = typename PP::template right_shift_result<T, U>;
using cc_type = typename PP::template comparison_result<T, U>;
using baw_type = typename PP::template bitwise_and_result<T, U>;
using bow_type = typename PP::template bitwise_or_result<T, U>;
using bxw_type = typename PP::template bitwise_xor_result<T, U>;
};
} // safe_numerics
} // boost
#endif // BOOST_NUMERIC_CONCEPT_EXCEPTION_POLICY_HPP

View file

@ -0,0 +1,34 @@
#ifndef BOOST_NUMERIC_CONCEPT_SAFE_NUMERIC_HPP
#define BOOST_NUMERIC_CONCEPT_SAFE_NUMERIC_HPP
// Copyright (c) 2015 Robert Ramey
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <limits>
#include <typetraits>
#include <boost/concept/usage.hpp>
#include "concept/numeric.hpp"
namespace boost {
namespace safe_numerics {
template<class T>
struct SafeNumeric : public Numeric<T> {
static_assert(
is_safe<T>::value,
"std::numeric_limits<T> has not been specialized for this type"
);
BOOST_CONCEPT_USAGE(SafeNumeric){
using t1 = get_exception_policy<T>;
using t2 = get_promotion_policy<T>;
using t3 = base_type<T>;
}
};
} // safe_numerics
} // boost
#endif // BOOST_NUMERIC_CONCEPT_SAFE_NUMERIC_HPP

197
boost/safe_numerics/cpp.hpp Normal file
View file

@ -0,0 +1,197 @@
#ifndef BOOST_NUMERIC_CPP_HPP
#define BOOST_NUMERIC_CPP_HPP
// Copyright (c) 2012 Robert Ramey
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// 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.
#include <type_traits> // integral constant, remove_cv, conditional
#include <limits>
#include <boost/integer.hpp> // integer type selection
#include "safe_common.hpp"
#include "checked_result.hpp"
namespace boost {
namespace safe_numerics {
// in C++ the following rules govern integer arithmetic
// This policy is use to emulate another compiler/machine architecture
// For example, a Z80 has 8 bit char, 16 bit short, 16 bit int, 32 bit long. So one
// would use cpp<8, 16, 16, 32, 32> to test programs destined to run on a Z80
// Follow section 5 of the standard.
template<
int CharBits,
int ShortBits,
int IntBits,
int LongBits,
int LongLongBits
>
struct cpp {
public:
using local_char_type = typename boost::int_t<CharBits>::exact;
using local_short_type = typename boost::int_t<ShortBits>::exact;
using local_int_type = typename boost::int_t<IntBits>::exact;
using local_long_type = typename boost::int_t<LongBits>::exact;
using local_long_long_type = typename boost::int_t<LongLongBits>::exact;
template<class T>
using rank =
typename std::conditional<
std::is_same<local_char_type, typename std::make_signed<T>::type>::value,
std::integral_constant<int, 1>,
typename std::conditional<
std::is_same<local_short_type, typename std::make_signed<T>::type>::value,
std::integral_constant<int, 2>,
typename std::conditional<
std::is_same<local_int_type, typename std::make_signed<T>::type>::value,
std::integral_constant<int, 3>,
typename std::conditional<
std::is_same<local_long_type, typename std::make_signed<T>::type>::value,
std::integral_constant<int, 4>,
typename std::conditional<
std::is_same<local_long_long_type, typename std::make_signed<T>::type>::value,
std::integral_constant<int, 5>,
std::integral_constant<int, 6> // catch all - never promote integral
>::type >::type >::type >::type >::type;
// section 4.5 integral promotions
// convert smaller of two types to the size of the larger
template<class T, class U>
using higher_ranked_type = typename std::conditional<
(rank<T>::value < rank<U>::value),
U,
T
>::type;
template<class T, class U>
using copy_sign = typename std::conditional<
std::is_signed<U>::value,
typename std::make_signed<T>::type,
typename std::make_unsigned<T>::type
>::type;
template<class T>
using integral_promotion = copy_sign<
higher_ranked_type<local_int_type, T>,
T
>;
// note presumption that T & U don't have he same sign
// if that's not true, these won't work
template<class T, class U>
using select_signed = typename std::conditional<
std::numeric_limits<T>::is_signed,
T,
U
>::type;
template<class T, class U>
using select_unsigned = typename std::conditional<
std::numeric_limits<T>::is_signed,
U,
T
>::type;
// section 5 clause 11 - usual arithmetic conversions
template<typename T, typename U>
using usual_arithmetic_conversions =
// clause 0 - if both operands have the same type
typename std::conditional<
std::is_same<T, U>::value,
// no further conversion is needed
T,
// clause 1 - otherwise if both operands have the same sign
typename std::conditional<
std::numeric_limits<T>::is_signed
== std::numeric_limits<U>::is_signed,
// convert to the higher ranked type
higher_ranked_type<T, U>,
// clause 2 - otherwise if the rank of he unsigned type exceeds
// the rank of the of the signed type
typename std::conditional<
rank<select_unsigned<T, U>>::value
>= rank< select_signed<T, U>>::value,
// use unsigned type
select_unsigned<T, U>,
// clause 3 - otherwise if the type of the signed integer type can
// represent all the values of the unsigned type
typename std::conditional<
std::numeric_limits< select_signed<T, U>>::digits >=
std::numeric_limits< select_unsigned<T, U>>::digits,
// use signed type
select_signed<T, U>,
// clause 4 - otherwise use unsigned version of the signed type
std::make_signed< select_signed<T, U>>
>::type >::type >::type
>;
template<typename T, typename U>
using result_type = typename usual_arithmetic_conversions<
integral_promotion<typename base_type<T>::type>,
integral_promotion<typename base_type<U>::type>
>::type;
public:
template<typename T, typename U>
struct addition_result {
using type = result_type<T, U>;
};
template<typename T, typename U>
struct subtraction_result {
using type = result_type<T, U>;
};
template<typename T, typename U>
struct multiplication_result {
using type = result_type<T, U>;
};
template<typename T, typename U>
struct division_result {
using type = result_type<T, U>;
};
template<typename T, typename U>
struct modulus_result {
using type = result_type<T, U>;
};
// note: comparison_result (<, >, ...) is special.
// The return value is always a bool. The type returned here is
// the intermediate type applied to make the values comparable.
template<typename T, typename U>
struct comparison_result {
using type = result_type<T, U>;
};
template<typename T, typename U>
struct left_shift_result {
using type = result_type<T, U>;
};
template<typename T, typename U>
struct right_shift_result {
using type = result_type<T, U>;
};
template<typename T, typename U>
struct bitwise_and_result {
using type = result_type<T, U>;
};
template<typename T, typename U>
struct bitwise_or_result {
using type = result_type<T, U>;
};
template<typename T, typename U>
struct bitwise_xor_result {
using type = result_type<T, U>;
};
};
} // safe_numerics
} // boost
#endif // BOOST_NUMERIC_cpp_HPP

View file

@ -0,0 +1,190 @@
#ifndef BOOST_NUMERIC_EXCEPTION
#define BOOST_NUMERIC_EXCEPTION
// Copyright (c) 2012 Robert Ramey
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// contains error indicators for results of doing checked
// arithmetic on native C++ types
#include <algorithm>
#include <system_error> // error_code, system_error
#include <string>
#include <cassert>
#include <cstdint> // std::uint8_t
// Using the system_error code facility. This facility is more complex
// than meets the eye. To fully understand what out intent here is,
// review http://blog.think-async.com/2010/04/system-error-support-in-c0x-part-5.html
// "Giving context-specific meaning to generic error codes"
namespace boost {
namespace safe_numerics {
// errors codes for safe numerics
// in spite of the similarity, this list is distinct from the exceptions
// listed in documentation for std::exception.
// note: Don't reorder these. Code in the file checked_result_operations.hpp
// depends upon this order !!!
enum class safe_numerics_error : std::uint8_t {
success = 0,
positive_overflow_error, // result is above representational maximum
negative_overflow_error, // result is below representational minimum
domain_error, // one operand is out of valid range
range_error, // result cannot be produced for this operation
precision_overflow_error, // result lost precision
underflow_error, // result is too small to be represented
negative_value_shift, // negative value in shift operator
negative_shift, // shift a negative value
shift_too_large, // l/r shift exceeds variable size
uninitialized_value // creating of uninitialized value
};
const std::uint8_t safe_numerics_casting_error_count =
static_cast<std::uint8_t>(safe_numerics_error::domain_error) + 1;
const std::uint8_t safe_numerics_error_count =
static_cast<std::uint8_t>(safe_numerics_error::uninitialized_value) + 1;
} // safe_numerics
} // boost
namespace std {
template <>
struct is_error_code_enum<boost::safe_numerics::safe_numerics_error>
: public true_type {};
} // std
namespace boost {
namespace safe_numerics {
const class : public std::error_category {
public:
virtual const char* name() const noexcept{
return "safe numerics error";
}
virtual std::string message(int ev) const {
switch(static_cast<safe_numerics_error>(ev)){
case safe_numerics_error::success:
return "success";
case safe_numerics_error::positive_overflow_error:
return "positive overflow error";
case safe_numerics_error::negative_overflow_error:
return "negative overflow error";
case safe_numerics_error::underflow_error:
return "underflow error";
case safe_numerics_error::range_error:
return "range error";
case safe_numerics_error::precision_overflow_error:
return "precision_overflow_error";
case safe_numerics_error::domain_error:
return "domain error";
case safe_numerics_error::negative_shift:
return "negative shift";
case safe_numerics_error::negative_value_shift:
return "negative value shift";
case safe_numerics_error::shift_too_large:
return "shift too large";
case safe_numerics_error::uninitialized_value:
return "uninitialized value";
default:
assert(false);
}
return ""; // suppress bogus warning
}
} safe_numerics_error_category {};
// constexpr - damn, can't use constexpr due to std::error_code
inline std::error_code make_error_code(const safe_numerics_error & e){
return std::error_code(static_cast<int>(e), safe_numerics_error_category);
}
// actions for error_codes for safe numerics. I've leveraged on
// error_condition in order to do this. I'm not sure this is a good
// idea or not.
enum class safe_numerics_actions {
no_action = 0,
uninitialized_value,
arithmetic_error,
implementation_defined_behavior,
undefined_behavior
};
} // safe_numerics
} // boost
namespace std {
template <>
struct is_error_condition_enum<boost::safe_numerics::safe_numerics_actions>
: public true_type {};
} // std
namespace boost {
namespace safe_numerics {
const class : public std::error_category {
public:
virtual const char* name() const noexcept {
return "safe numerics error group";
}
virtual std::string message(int) const {
return "safe numerics error group";
}
// return true if a given error code corresponds to a
// given safe numeric action
virtual bool equivalent(
const std::error_code & code,
int condition
) const noexcept {
if(code.category() != safe_numerics_error_category)
return false;
switch (static_cast<safe_numerics_actions>(condition)){
case safe_numerics_actions::no_action:
return code == safe_numerics_error::success;
case safe_numerics_actions::uninitialized_value:
return code == safe_numerics_error::uninitialized_value;
case safe_numerics_actions::arithmetic_error:
return code == safe_numerics_error::positive_overflow_error
|| code == safe_numerics_error::negative_overflow_error
|| code == safe_numerics_error::underflow_error
|| code == safe_numerics_error::range_error
|| code == safe_numerics_error::domain_error;
case safe_numerics_actions::implementation_defined_behavior:
return code == safe_numerics_error::negative_value_shift
|| code == safe_numerics_error::negative_shift
|| code == safe_numerics_error::shift_too_large;
case safe_numerics_actions::undefined_behavior:
return false;
default:
;
}
// should never arrive here
assert(false);
// suppress bogus warning
return false;
}
} safe_numerics_actions_category {};
// the following function is used to "finish" implementation of conversion
// of safe_numerics_error to std::error_condition. At least for now, this
// isn't being used and defining here it can lead duplicate symbol errors
// depending on the compiler. So suppress it until further notice
#if 0
std::error_condition make_error_condition(const safe_numerics_error & e) {
return std::error_condition(
static_cast<int>(e),
safe_numerics_error_category
);
}
#endif
} // safe_numerics
} // boost
#endif // BOOST_NUMERIC_CHECKED_RESULT

View file

@ -0,0 +1,230 @@
#ifndef BOOST_NUMERIC_EXCEPTION_POLICIES_HPP
#define BOOST_NUMERIC_EXCEPTION_POLICIES_HPP
// Copyright (c) 2015 Robert Ramey
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <boost/mp11.hpp>
#include <boost/config.hpp> // BOOST_NO_EXCEPTIONS
#include "exception.hpp"
namespace boost {
namespace safe_numerics {
template<
typename AE,
typename IDB,
typename UB,
typename UV
>
struct exception_policy {
static constexpr void on_arithmetic_error(
const safe_numerics_error & e,
const char * msg
){
AE(e, msg);
}
static constexpr void on_implementation_defined_behavior(
const safe_numerics_error & e,
const char * msg
){
IDB(e, msg);
}
static constexpr void on_undefined_behavior(
const safe_numerics_error & e,
const char * msg
){
UB(e, msg);
}
static constexpr void on_uninitialized_value(
const safe_numerics_error & e,
const char * msg
){
UV(e, msg);
}
};
////////////////////////////////////////////////////////////////////////////////
// pre-made error action handers
// ignore any error and just return.
struct ignore_exception {
constexpr ignore_exception(const safe_numerics_error &, const char * ){}
};
// emit compile time error if this is invoked.
struct trap_exception {
#if 1
//constexpr trap_exception(const safe_numerics_error & e, const char * );
trap_exception() = delete;
trap_exception(const trap_exception &) = delete;
trap_exception(trap_exception &&) = delete;
#endif
};
// If an exceptional condition is detected at runtime throw the exception.
struct throw_exception {
#ifndef BOOST_NO_EXCEPTIONS
throw_exception(const safe_numerics_error & e, const char * message){
throw std::system_error(std::error_code(e), message);
}
#else
trap_exception(const safe_numerics_error & e, const char * message);
#endif
};
// given an error code - return the action code which it corresponds to.
constexpr safe_numerics_actions
make_safe_numerics_action(const safe_numerics_error & e){
// we can't use standard algorithms since we want this to be constexpr
// this brute force solution is simple and pretty fast anyway
switch(e){
case safe_numerics_error::negative_overflow_error:
case safe_numerics_error::underflow_error:
case safe_numerics_error::range_error:
case safe_numerics_error::domain_error:
case safe_numerics_error::positive_overflow_error:
case safe_numerics_error::precision_overflow_error:
return safe_numerics_actions::arithmetic_error;
case safe_numerics_error::negative_value_shift:
case safe_numerics_error::negative_shift:
case safe_numerics_error::shift_too_large:
return safe_numerics_actions::implementation_defined_behavior;
case safe_numerics_error::uninitialized_value:
return safe_numerics_actions::uninitialized_value;
case safe_numerics_error::success:
return safe_numerics_actions::no_action;
default:
assert(false);
}
// should never arrive here
//include to suppress bogus warning
return safe_numerics_actions::no_action;
}
////////////////////////////////////////////////////////////////////////////////
// compile time error dispatcher
// note slightly baroque implementation of a compile time switch statement
// which instatiates only those cases which are actually invoked. This is
// motivated to implement the "trap" functionality which will generate a syntax
// error if and only a function which might fail is called.
namespace dispatch_switch {
template<class EP, safe_numerics_actions>
struct dispatch_case {};
template<class EP>
struct dispatch_case<EP, safe_numerics_actions::uninitialized_value> {
constexpr static void invoke(const safe_numerics_error & e, const char * msg){
EP::on_uninitialized_value(e, msg);
}
};
template<class EP>
struct dispatch_case<EP, safe_numerics_actions::arithmetic_error> {
constexpr static void invoke(const safe_numerics_error & e, const char * msg){
EP::on_arithmetic_error(e, msg);
}
};
template<class EP>
struct dispatch_case<EP, safe_numerics_actions::implementation_defined_behavior> {
constexpr static void invoke(const safe_numerics_error & e, const char * msg){
EP::on_implementation_defined_behavior(e, msg);
}
};
template<class EP>
struct dispatch_case<EP, safe_numerics_actions::undefined_behavior> {
constexpr static void invoke(const safe_numerics_error & e, const char * msg){
EP::on_undefined_behavior(e, msg);
}
};
} // dispatch_switch
template<class EP, safe_numerics_error E>
constexpr void
dispatch(const char * msg){
constexpr safe_numerics_actions a = make_safe_numerics_action(E);
dispatch_switch::dispatch_case<EP, a>::invoke(E, msg);
}
template<class EP, class R>
class dispatch_and_return {
public:
template<safe_numerics_error E>
constexpr static checked_result<R> invoke(
char const * const & msg
) {
dispatch<EP, E>(msg);
return checked_result<R>(E, msg);
}
};
////////////////////////////////////////////////////////////////////////////////
// pre-made error policy classes
// loose exception
// - throw on arithmetic errors
// - ignore other errors.
// Some applications ignore these issues and still work and we don't
// want to update them.
using loose_exception_policy = exception_policy<
throw_exception, // arithmetic error
ignore_exception, // implementation defined behavior
ignore_exception, // undefined behavior
ignore_exception // uninitialized value
>;
// loose trap
// same as above in that it doesn't check for various undefined behaviors
// but traps at compile time for hard arithmetic errors. This policy
// would be suitable for older embedded systems which depend on
// bit manipulation operations to work.
using loose_trap_policy = exception_policy<
trap_exception, // arithmetic error
ignore_exception, // implementation defined behavior
ignore_exception, // undefined behavior
ignore_exception // uninitialized value
>;
// strict exception
// - throw at runtime on any kind of error
// recommended for new code. Check everything at compile time
// if possible and runtime if necessary. Trap or Throw as
// appropriate. Should guarantee code to be portable across
// architectures.
using strict_exception_policy = exception_policy<
throw_exception,
throw_exception,
throw_exception,
ignore_exception
>;
// strict trap
// Same as above but requires code to be written in such a way as to
// make it impossible for errors to occur. This naturally will require
// extra coding effort but might be justified for embedded and/or
// safety critical systems.
using strict_trap_policy = exception_policy<
trap_exception,
trap_exception,
trap_exception,
trap_exception
>;
// default policy
// One would use this first. After experimentation, one might
// replace some actions with ignore_exception
using default_exception_policy = strict_exception_policy;
} // namespace safe_numerics
} // namespace boost
#endif // BOOST_NUMERIC_EXCEPTION_POLICIES_HPP

View file

@ -0,0 +1,310 @@
#ifndef BOOST_NUMERIC_INTERVAL_HPP
#define BOOST_NUMERIC_INTERVAL_HPP
// Copyright (c) 2012 Robert Ramey
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <limits>
#include <cassert>
#include <type_traits>
#include <initializer_list>
#include <algorithm> // minmax, min, max
#include <boost/logic/tribool.hpp>
#include "utility.hpp" // log
// from stack overflow
// http://stackoverflow.com/questions/23815138/implementing-variadic-min-max-functions
namespace boost {
namespace safe_numerics {
template<typename R>
struct interval {
const R l;
const R u;
template<typename T>
constexpr interval(const T & lower, const T & upper) :
l(lower),
u(upper)
{
// assert(static_cast<bool>(l <= u));
}
template<typename T>
constexpr interval(const std::pair<T, T> & p) :
l(p.first),
u(p.second)
{}
template<class T>
constexpr interval(const interval<T> & rhs) :
l(rhs.l),
u(rhs.u)
{}
constexpr interval();
// return true if this interval contains the given point
constexpr tribool includes(const R & t) const {
return l <= t && t <= u;
}
// if this interval contains every point found in some other inteval t
// return true
// otherwise
// return false or indeterminate
constexpr tribool includes(const interval<R> & t) const {
return u >= t.u && l <= t.l;
}
// return true if this interval contains the given point
constexpr tribool excludes(const R & t) const {
return t < l || t > u;
}
// if this interval excludes every point found in some other inteval t
// return true
// otherwise
// return false or indeterminate
constexpr tribool excludes(const interval<R> & t) const {
return t.u < l || u < t.l;
}
};
template<class R>
constexpr interval<R> make_interval(){
return interval<R>();
}
template<class R>
constexpr interval<R> make_interval(const R & r){
return interval<R>(r, r);
}
template<class R>
constexpr interval<R>::interval() :
l(std::numeric_limits<R>::lowest()),
u(std::numeric_limits<R>::max())
{}
// account for the fact that for floats and doubles
// the most negative value is called "lowest" rather
// than min
template<>
constexpr interval<float>::interval() :
l(std::numeric_limits<float>::lowest()),
u(std::numeric_limits<float>::max())
{}
template<>
constexpr interval<double>::interval() :
l(std::numeric_limits<double>::lowest()),
u(std::numeric_limits<double>::max())
{}
template<typename T>
constexpr interval<T> operator+(const interval<T> & t, const interval<T> & u){
// adapted from https://en.wikipedia.org/wiki/Interval_arithmetic
return {t.l + u.l, t.u + u.u};
}
template<typename T>
constexpr interval<T> operator-(const interval<T> & t, const interval<T> & u){
// adapted from https://en.wikipedia.org/wiki/Interval_arithmetic
return {t.l - u.u, t.u - u.l};
}
template<typename T>
constexpr interval<T> operator*(const interval<T> & t, const interval<T> & u){
// adapted from https://en.wikipedia.org/wiki/Interval_arithmetic
return utility::minmax<T>(
std::initializer_list<T> {
t.l * u.l,
t.l * u.u,
t.u * u.l,
t.u * u.u
}
);
}
// interval division
// note: presumes 0 is not included in the range of the denominator
template<typename T>
constexpr interval<T> operator/(const interval<T> & t, const interval<T> & u){
assert(static_cast<bool>(u.excludes(T(0))));
return utility::minmax<T>(
std::initializer_list<T> {
t.l / u.l,
t.l / u.u,
t.u / u.l,
t.u / u.u
}
);
}
// modulus of two intervals. This will give a new range of for the modulus.
// note: presumes 0 is not included in the range of the denominator
template<typename T>
constexpr interval<T> operator%(const interval<T> & t, const interval<T> & u){
assert(static_cast<bool>(u.excludes(T(0))));
return utility::minmax<T>(
std::initializer_list<T> {
t.l % u.l,
t.l % u.u,
t.u % u.l,
t.u % u.u
}
);
}
template<typename T>
constexpr interval<T> operator<<(const interval<T> & t, const interval<T> & u){
// static_assert(std::is_integral<T>::value, "left shift only defined for integral type");
//return interval<T>{t.l << u.l, t.u << u.u};
return utility::minmax<T>(
std::initializer_list<T> {
t.l << u.l,
t.l << u.u,
t.u << u.l,
t.u << u.u
}
);
}
template<typename T>
constexpr interval<T> operator>>(const interval<T> & t, const interval<T> & u){
// static_assert(std::is_integral<T>::value, "right shift only defined for integral type");
//return interval<T>{t.l >> u.u, t.u >> u.l};
return utility::minmax<T>(
std::initializer_list<T> {
t.l >> u.l,
t.l >> u.u,
t.u >> u.l,
t.u >> u.u
}
);
}
// union of two intervals
template<typename T>
constexpr interval<T> operator|(const interval<T> & t, const interval<T> & u){
const T & rl = std::min(t.l, u.l);
const T & ru = std::max(t.u, u.u);
return interval<T>(rl, ru);
}
// intersection of two intervals
template<typename T>
constexpr interval<T> operator&(const interval<T> & t, const interval<T> & u){
const T & rl = std::max(t.l, u.l);
const T & ru = std::min(t.u, u.u);
return interval<T>(rl, ru);
}
// determine whether two intervals intersect
template<typename T>
constexpr boost::logic::tribool intersect(const interval<T> & t, const interval<T> & u){
return t.u >= u.l || t.l <= u.u;
}
template<typename T>
constexpr boost::logic::tribool operator<(
const interval<T> & t,
const interval<T> & u
){
return
// if every element in t is less than every element in u
t.u < u.l ? boost::logic::tribool(true):
// if every element in t is greater than every element in u
t.l > u.u ? boost::logic::tribool(false):
// otherwise some element(s) in t are greater than some element in u
boost::logic::indeterminate
;
}
template<typename T>
constexpr boost::logic::tribool operator>(
const interval<T> & t,
const interval<T> & u
){
return
// if every element in t is greater than every element in u
t.l > u.u ? boost::logic::tribool(true) :
// if every element in t is less than every element in u
t.u < u.l ? boost::logic::tribool(false) :
// otherwise some element(s) in t are greater than some element in u
boost::logic::indeterminate
;
}
template<typename T>
constexpr bool operator==(
const interval<T> & t,
const interval<T> & u
){
// intervals have the same limits
return t.l == u.l && t.u == u.u;
}
template<typename T>
constexpr bool operator!=(
const interval<T> & t,
const interval<T> & u
){
return ! (t == u);
}
template<typename T>
constexpr boost::logic::tribool operator<=(
const interval<T> & t,
const interval<T> & u
){
return ! (t > u);
}
template<typename T>
constexpr boost::logic::tribool operator>=(
const interval<T> & t,
const interval<T> & u
){
return ! (t < u);
}
} // safe_numerics
} // boost
#include <iosfwd>
namespace std {
template<typename CharT, typename Traits, typename T>
inline std::basic_ostream<CharT, Traits> &
operator<<(
std::basic_ostream<CharT, Traits> & os,
const boost::safe_numerics::interval<T> & i
){
return os << '[' << i.l << ',' << i.u << ']';
}
template<typename CharT, typename Traits>
inline std::basic_ostream<CharT, Traits> &
operator<<(
std::basic_ostream<CharT, Traits> & os,
const boost::safe_numerics::interval<unsigned char> & i
){
os << "[" << (unsigned)i.l << "," << (unsigned)i.u << "]";
return os;
}
template<typename CharT, typename Traits>
inline std::basic_ostream<CharT, Traits> &
operator<<(
std::basic_ostream<CharT, Traits> & os,
const boost::safe_numerics::interval<signed char> & i
){
os << "[" << (int)i.l << "," << (int)i.u << "]";
return os;
}
} // std
#endif // BOOST_NUMERIC_INTERVAL_HPP

View file

@ -0,0 +1,110 @@
#ifndef BOOST_NUMERIC_NATIVE_HPP
#define BOOST_NUMERIC_NATIVE_HPP
// Copyright (c) 2012 Robert Ramey
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <type_traits>
#include <limits>
// policy which creates results types and values equal to that of C++ promotions.
// When used in conjunction with a desired exception policy, traps errors but
// does not otherwise alter the results produced by the program using it.
namespace boost {
namespace safe_numerics {
struct native {
public:
// arithmetic operators
template<typename T, typename U>
struct addition_result {
using type = decltype(
typename base_type<T>::type()
+ typename base_type<U>::type()
);
};
template<typename T, typename U>
struct subtraction_result {
using type = decltype(
typename base_type<T>::type()
- typename base_type<U>::type()
);
};
template<typename T, typename U>
struct multiplication_result {
using type = decltype(
typename base_type<T>::type()
* typename base_type<U>::type()
);
};
template<typename T, typename U>
struct division_result {
using type = decltype(
typename base_type<T>::type()
/ typename base_type<U>::type()
);
};
template<typename T, typename U>
struct modulus_result {
using type = decltype(
typename base_type<T>::type()
% typename base_type<U>::type()
);
};
// note: comparison_result (<, >, ...) is special.
// The return value is always a bool. The type returned here is
// the intermediate type applied to make the values comparable.
template<typename T, typename U>
struct comparison_result {
using type = decltype(
typename base_type<T>::type()
+ typename base_type<U>::type()
);
};
// shift operators
template<typename T, typename U>
struct left_shift_result {
using type = decltype(
typename base_type<T>::type()
<< typename base_type<U>::type()
);
};
template<typename T, typename U>
struct right_shift_result {
using type = decltype(
typename base_type<T>::type()
>> typename base_type<U>::type()
);
};
// bitwise operators
template<typename T, typename U>
struct bitwise_or_result {
using type = decltype(
typename base_type<T>::type()
| typename base_type<U>::type()
);
};
template<typename T, typename U>
struct bitwise_and_result {
using type = decltype(
typename base_type<T>::type()
& typename base_type<U>::type()
);
};
template<typename T, typename U>
struct bitwise_xor_result {
using type = decltype(
typename base_type<T>::type()
^ typename base_type<U>::type()
);
};
};
} // safe_numerics
} // boost
#endif // BOOST_NUMERIC_NATIVE_HPP

View file

@ -0,0 +1,71 @@
#ifndef BOOST_RANGE_VALUE_HPP
#define BOOST_RANGE_VALUE_HPP
// Copyright (c) 2015 Robert Ramey
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <iosfwd>
// print range and value to a standard stream
// make a simple wrapper
template<typename T>
struct range_value {
// type requirement - a numeric type
const T & m_t;
constexpr range_value(const T & t)
: m_t(t)
{}
};
template<typename T>
constexpr range_value<T> make_range_value(const T & t){
return range_value<T>(t);
}
#include "interval.hpp"
template<
class CharT,
class Traits,
class T
>
std::basic_ostream<CharT, Traits> & operator<<(
std::basic_ostream<CharT, Traits> & os,
const range_value<T> & t
){
return os
<< boost::safe_numerics::make_interval<T>()
<< t.m_t;
}
template<typename T>
struct result_display {
const T & m_t;
result_display(const T & t) :
m_t(t)
{}
};
template<typename T>
result_display<T> make_result_display(const T & t){
return result_display<T>(t);
}
template<typename CharT, typename Traits, typename T>
inline std::basic_ostream<CharT, Traits> &
operator<<(
std::basic_ostream<CharT, Traits> & os,
const result_display<T> & r
){
return os
<< std::hex
<< make_range_value(r.m_t)
<< "(" << std::dec << r.m_t << ")";
}
#endif // BOOST_RANGE_VALUE_HPP

View file

@ -0,0 +1,349 @@
#ifndef BOOST_NUMERIC_SAFE_BASE_HPP
#define BOOST_NUMERIC_SAFE_BASE_HPP
// Copyright (c) 2012 Robert Ramey
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <limits>
#include <type_traits> // is_integral, enable_if, conditional
#include <boost/config.hpp> // BOOST_CLANG
#include "concept/exception_policy.hpp"
#include "concept/promotion_policy.hpp"
#include "safe_common.hpp"
#include "exception_policies.hpp"
#include "boost/concept/assert.hpp"
namespace boost {
namespace safe_numerics {
/////////////////////////////////////////////////////////////////
// forward declarations to support friend function declarations
// in safe_base
template<
class Stored,
Stored Min,
Stored Max,
class P, // promotion polic
class E // exception policy
>
class safe_base;
template<
class T,
T Min,
T Max,
class P,
class E
>
struct is_safe<safe_base<T, Min, Max, P, E> > : public std::true_type
{};
template<
class T,
T Min,
T Max,
class P,
class E
>
struct get_promotion_policy<safe_base<T, Min, Max, P, E> > {
using type = P;
};
template<
class T,
T Min,
T Max,
class P,
class E
>
struct get_exception_policy<safe_base<T, Min, Max, P, E> > {
using type = E;
};
template<
class T,
T Min,
T Max,
class P,
class E
>
struct base_type<safe_base<T, Min, Max, P, E> > {
using type = T;
};
template<
class T,
T Min,
T Max,
class P,
class E
>
constexpr T base_value(
const safe_base<T, Min, Max, P, E> & st
) {
return static_cast<T>(st);
}
template<
typename T,
T N,
class P, // promotion policy
class E // exception policy
>
class safe_literal_impl;
// works for both GCC and clang
#if BOOST_CLANG==1
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmismatched-tags"
#endif
/////////////////////////////////////////////////////////////////
// Main implementation
template<
class Stored,
Stored Min,
Stored Max,
class P, // promotion polic
class E // exception policy
>
class safe_base {
private:
BOOST_CONCEPT_ASSERT((PromotionPolicy<P>));
BOOST_CONCEPT_ASSERT((ExceptionPolicy<E>));
Stored m_t;
template<
class StoredX,
StoredX MinX,
StoredX MaxX,
class PX, // promotion polic
class EX // exception policy
>
friend class safe_base;
friend class std::numeric_limits<safe_base>;
template<class T>
constexpr Stored validated_cast(const T & t) const;
template<typename T, T N, class P1, class E1>
constexpr Stored validated_cast(
const safe_literal_impl<T, N, P1, E1> & t
) const;
// stream support
template<class CharT, class Traits>
void output(std::basic_ostream<CharT, Traits> & os) const;
// note usage of friend declaration to mark function as
// a global function rather than a member function. If
// this is not done, the compiler will confuse this with
// a member operator overload on the << operator. Weird
// I know. But it's documented here
// http://en.cppreference.com/w/cpp/language/friend
// under the heading "Template friend operators"
template<class CharT, class Traits>
friend std::basic_ostream<CharT, Traits> &
operator<<(
std::basic_ostream<CharT, Traits> & os,
const safe_base & t
){
t.output(os);
return os;
}
template<class CharT, class Traits>
void input(std::basic_istream<CharT, Traits> & is);
// see above
template<class CharT, class Traits>
friend inline std::basic_istream<CharT, Traits> &
operator>>(
std::basic_istream<CharT, Traits> & is,
safe_base & t
){
t.input(is);
return is;
}
public:
////////////////////////////////////////////////////////////
// constructors
struct skip_validation{};
constexpr explicit safe_base(const Stored & rhs, skip_validation);
constexpr safe_base();
// construct an instance of a safe type
// from an instance of a convertible underlying type.
template<class T>
constexpr /*explicit*/ safe_base(
const T & t,
typename std::enable_if<
is_safe<T>::value,
bool
>::type = true
);
template<class T>
constexpr /*explicit*/ safe_base(
const T & t,
typename std::enable_if<
std::is_integral<T>::value,
bool
>::type = true
);
template<class T, T value>
constexpr /*explicit*/ safe_base(
const std::integral_constant<T, value> &
);
// note: Rule of Five. Supply all or none of the following
// a) user-defined destructor
~safe_base() = default;
// b) copy-constructor
constexpr safe_base(const safe_base &) = default;
// c) copy-assignment
constexpr safe_base & operator=(const safe_base &) = default;
// d) move constructor
constexpr safe_base(safe_base &&) = default;
// e) move assignment operator
constexpr safe_base & operator=(safe_base &&) = default;
/////////////////////////////////////////////////////////////////
// casting operators for intrinsic integers
// convert to any type which is not safe. safe types need to be
// excluded to prevent ambiguous function selection which
// would otherwise occur. validity of safe types is checked in
// the constructor of safe types
template<
class R,
typename std::enable_if<
! boost::safe_numerics::is_safe<R>::value,
int
>::type = 0
>
constexpr /*explicit*/ operator R () const;
constexpr /*explicit*/ operator Stored () const;
/////////////////////////////////////////////////////////////////
// modification binary operators
template<class T>
constexpr safe_base &
operator=(const T & rhs){
m_t = validated_cast(rhs);
return *this;
}
// required to passify VS2017
constexpr safe_base &
operator=(const Stored & rhs){
m_t = validated_cast(rhs);
return *this;
}
// mutating unary operators
safe_base & operator++(){ // pre increment
return *this = *this + 1;
}
safe_base & operator--(){ // pre decrement
return *this = *this - 1;
}
safe_base operator++(int){ // post increment
safe_base old_t = *this;
++(*this);
return old_t;
}
safe_base operator--(int){ // post decrement
safe_base old_t = *this;
--(*this);
return old_t;
}
// non mutating unary operators
constexpr auto 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.
/* section 5.3.1 &8 of the C++ standard
The operand of the unary - operator shall have arithmetic or unscoped
enumeration type and the result is the negation of its operand. Integral
promotion is performed on integral or enumeration operands. The negative
of an unsigned quantity is computed by subtracting its value from 2n,
where n is the number of bits in the promoted operand. The type of the
result is the type of the promoted operand.
*/
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;
}
/* section 5.3.1 &10 of the C++ standard
The operand of ~ shall have integral or unscoped enumeration type;
the result is the ones complement of its operand. Integral promotions
are performed. The type of the result is the type of the promoted operand.
*/
constexpr auto operator~() const { // complement
return ~Stored(0u) ^ *this;
}
};
} // safe_numerics
} // boost
/////////////////////////////////////////////////////////////////
// numeric limits for safe<int> etc.
#include <limits>
namespace std {
template<
class T,
T Min,
T Max,
class P,
class E
>
class numeric_limits<boost::safe_numerics::safe_base<T, Min, Max, P, E> >
: public std::numeric_limits<T>
{
using SB = boost::safe_numerics::safe_base<T, Min, Max, P, E>;
public:
constexpr static SB lowest() noexcept {
return SB(Min, typename SB::skip_validation());
}
constexpr static SB min() noexcept {
return SB(Min, typename SB::skip_validation());
}
constexpr static SB max() noexcept {
return SB(Max, typename SB::skip_validation());
}
};
} // std
#if BOOST_CLANG==1
#pragma GCC diagnostic pop
#endif
#endif // BOOST_NUMERIC_SAFE_BASE_HPP

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,44 @@
#ifndef BOOST_NUMERIC_SAFE_COMMON_HPP
#define BOOST_NUMERIC_SAFE_COMMON_HPP
// Copyright (c) 2012 Robert Ramey
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <type_traits>
namespace boost {
namespace safe_numerics {
// default implementations for required meta-functions
template<typename T>
struct is_safe : public std::false_type
{};
template<typename T>
struct base_type {
using type = T;
};
template<class T>
constexpr const typename base_type<T>::type & base_value(const T & t) {
return static_cast<const typename base_type<T>::type & >(t);
}
template<typename T>
struct get_promotion_policy {
using type = void;
};
template<typename T>
struct get_exception_policy {
using type = void;
};
} // safe_numerics
} // boost
#endif // BOOST_NUMERIC_SAFE_COMMON_HPP

View file

@ -0,0 +1,180 @@
#ifndef BOOST_NUMERIC_SAFE_COMPARE_HPP
#define BOOST_NUMERIC_SAFE_COMPARE_HPP
// Copyright (c) 2012 Robert Ramey
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <type_traits>
#include <limits>
namespace boost {
namespace safe_numerics {
namespace safe_compare {
////////////////////////////////////////////////////
// safe comparison on primitive integral types
namespace safe_compare_detail {
template<typename T>
using make_unsigned = typename std::conditional<
std::is_signed<T>::value,
std::make_unsigned<T>,
T
>::type;
// both arguments unsigned or signed
template<bool TS, bool US>
struct less_than {
template<class T, class U>
constexpr static bool invoke(const T & t, const U & u){
return t < u;
}
};
// T unsigned, U signed
template<>
struct less_than<false, true> {
template<class T, class U>
constexpr static bool invoke(const T & t, const U & u){
return
(u < 0) ?
false
:
less_than<false, false>::invoke(
t,
static_cast<const typename make_unsigned<U>::type &>(u)
)
;
}
};
// T signed, U unsigned
template<>
struct less_than<true, false> {
template<class T, class U>
constexpr static bool invoke(const T & t, const U & u){
return
(t < 0) ?
true
:
less_than<false, false>::invoke(
static_cast<const typename make_unsigned<T>::type &>(t),
u
)
;
}
};
} // safe_compare_detail
template<class T, class U>
typename std::enable_if<
std::is_integral<T>::value && std::is_integral<U>::value,
bool
>::type
constexpr less_than(const T & lhs, const U & rhs) {
return safe_compare_detail::less_than<
std::is_signed<T>::value,
std::is_signed<U>::value
>::template invoke(lhs, rhs);
}
template<class T, class U>
typename std::enable_if<
std::is_floating_point<T>::value && std::is_floating_point<U>::value,
bool
>::type
constexpr less_than(const T & lhs, const U & rhs) {
return lhs < rhs;
}
template<class T, class U>
constexpr bool greater_than(const T & lhs, const U & rhs) {
return less_than(rhs, lhs);
}
template<class T, class U>
constexpr bool less_than_equal(const T & lhs, const U & rhs) {
return ! greater_than(lhs, rhs);
}
template<class T, class U>
constexpr bool greater_than_equal(const T & lhs, const U & rhs) {
return ! less_than(lhs, rhs);
}
namespace safe_compare_detail {
// both arguments unsigned or signed
template<bool TS, bool US>
struct equal {
template<class T, class U>
constexpr static bool invoke(const T & t, const U & u){
return t == u;
}
};
// T unsigned, U signed
template<>
struct equal<false, true> {
template<class T, class U>
constexpr static bool invoke(const T & t, const U & u){
return
(u < 0) ?
false
:
equal<false, false>::invoke(
t,
static_cast<const typename make_unsigned<U>::type &>(u)
)
;
}
};
// T signed, U unsigned
template<>
struct equal<true, false> {
template<class T, class U>
constexpr static bool invoke(const T & t, const U & u){
return
(t < 0) ?
false
:
equal<false, false>::invoke(
static_cast<const typename make_unsigned<T>::type &>(t),
u
)
;
}
};
} // safe_compare_detail
template<class T, class U>
typename std::enable_if<
std::is_integral<T>::value && std::is_integral<U>::value,
bool
>::type
constexpr equal(const T & lhs, const U & rhs) {
return safe_compare_detail::equal<
std::numeric_limits<T>::is_signed,
std::numeric_limits<U>::is_signed
>::template invoke(lhs, rhs);
}
template<class T, class U>
typename std::enable_if<
std::is_floating_point<T>::value && std::is_floating_point<U>::value,
bool
>::type
constexpr equal(const T & lhs, const U & rhs) {
return lhs == rhs;
}
template<class T, class U>
constexpr bool not_equal(const T & lhs, const U & rhs) {
return ! equal(lhs, rhs);
}
} // safe_compare
} // safe_numerics
} // boost
#endif // BOOST_NUMERIC_SAFE_COMPARE_HPP

View file

@ -0,0 +1,42 @@
#ifndef BOOST_NUMERIC_SAFE_INTEGER_HPP
#define BOOST_NUMERIC_SAFE_INTEGER_HPP
// Copyright (c) 2012 Robert Ramey
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// not actually used here - but needed for integer arithmetic
// so this is a good place to include it
#include "checked_integer.hpp"
#include "checked_result_operations.hpp"
#include "safe_base.hpp"
#include "safe_base_operations.hpp"
#include "native.hpp"
#include "exception_policies.hpp"
// specialization for meta functions with safe<T> argument
namespace boost {
namespace safe_numerics {
template <
class T,
class P = native,
class E = default_exception_policy
>
using safe = safe_base<
T,
::std::numeric_limits<T>::min(),
::std::numeric_limits<T>::max(),
P,
E
>;
} // safe_numerics
} // boost
#endif // BOOST_NUMERIC_SAFE_INTEGER_HPP

View file

@ -0,0 +1,260 @@
#ifndef BOOST_NUMERIC_SAFE_INTEGER_LITERAL_HPP
#define BOOST_NUMERIC_SAFE_INTEGER_LITERAL_HPP
// Copyright (c) 2012 Robert Ramey
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <cstdint> // for intmax_t/uintmax_t
#include <iosfwd>
#include <type_traits> // conditional, enable_if
#include <boost/mp11/utility.hpp>
#include "utility.hpp"
#include "safe_integer.hpp"
#include "checked_integer.hpp"
namespace boost {
namespace safe_numerics {
template<typename T, T N, class P, class E>
class safe_literal_impl;
template<typename T, T N, class P, class E>
struct is_safe<safe_literal_impl<T, N, P, E> > : public std::true_type
{};
template<typename T, T N, class P, class E>
struct get_promotion_policy<safe_literal_impl<T, N, P, E> > {
using type = P;
};
template<typename T, T N, class P, class E>
struct get_exception_policy<safe_literal_impl<T, N, P, E> > {
using type = E;
};
template<typename T, T N, class P, class E>
struct base_type<safe_literal_impl<T, N, P, E> > {
using type = T;
};
template<typename T, T N, class P, class E>
constexpr T base_value(
const safe_literal_impl<T, N, P, E> &
) {
return N;
}
template<typename CharT, typename Traits, typename T, T N, class P, class E>
inline std::basic_ostream<CharT, Traits> & operator<<(
std::basic_ostream<CharT, Traits> & os,
const safe_literal_impl<T, N, P, E> &
){
return os << (
(std::is_same<T, signed char>::value
|| std::is_same<T, unsigned char>::value
) ?
static_cast<int>(N)
:
N
);
}
template<typename T, T N, class P, class E>
class safe_literal_impl {
template<
class CharT,
class Traits
>
friend std::basic_ostream<CharT, Traits> & operator<<(
std::basic_ostream<CharT, Traits> & os,
const safe_literal_impl &
){
return os << (
(::std::is_same<T, signed char>::value
|| ::std::is_same<T, unsigned char>::value
|| ::std::is_same<T, wchar_t>::value
) ?
static_cast<int>(N)
:
N
);
};
public:
////////////////////////////////////////////////////////////
// constructors
// default constructor
constexpr safe_literal_impl(){}
/////////////////////////////////////////////////////////////////
// casting operators for intrinsic integers
// convert to any type which is not safe. safe types need to be
// excluded to prevent ambiguous function selection which
// would otherwise occur
template<
class R,
typename std::enable_if<
! boost::safe_numerics::is_safe<R>::value,
int
>::type = 0
>
constexpr operator R () const {
// if static values don't overlap, the program can never function
#if 1
constexpr const interval<R> r_interval;
static_assert(
! r_interval.excludes(N),
"safe type cannot be constructed with this type"
);
#endif
return validate_detail<
R,
std::numeric_limits<R>::min(),
std::numeric_limits<R>::max(),
E
>::return_value(*this);
}
// non mutating unary operators
constexpr safe_literal_impl<T, N, P, E> operator+() const { // unary plus
return safe_literal_impl<T, N, P, E>();
}
// after much consideration, I've permitted the resulting value of a unary
// - to change the type in accordance with the promotion policy.
// The C++ standard does invoke integral promotions so it's changing the type as well.
/* section 5.3.1 &8 of the C++ standard
The operand of the unary - operator shall have arithmetic or unscoped
enumeration type and the result is the negation of its operand. Integral
promotion is performed on integral or enumeration operands. The negative
of an unsigned quantity is computed by subtracting its value from 2n,
where n is the number of bits in the promoted operand. The type of the
result is the type of the promoted operand.
*/
template<
typename Tx, Tx Nx, typename = std::enable_if_t<! checked::minus(Nx).exception()>
>
constexpr auto minus_helper() const {
return safe_literal_impl<Tx, -N, P, E>();
}
constexpr auto operator-() const { // unary minus
return minus_helper<T, N>();
}
/* section 5.3.1 &10 of the C++ standard
The operand of ~ shall have integral or unscoped enumeration type;
the result is the ones complement of its operand. Integral promotions
are performed. The type of the result is the type of the promoted operand.
constexpr safe_literal_impl<T, checked::bitwise_not(N), P, E> operator~() const { // invert bits
return safe_literal_impl<T, checked::bitwise_not(N), P, E>();
}
*/
template<
typename Tx, Tx Nx, typename = std::enable_if_t<! checked::bitwise_not(Nx).exception()>
>
constexpr auto not_helper() const {
return safe_literal_impl<Tx, ~N, P, E>();
}
constexpr auto operator~() const { // unary minus
return not_helper<T, N>();
}
};
template<
std::intmax_t N,
class P = void,
class E = void
>
using safe_signed_literal = safe_literal_impl<
typename utility::signed_stored_type<N, N>,
N,
P,
E
>;
template<
std::uintmax_t N,
class P = void,
class E = void
>
using safe_unsigned_literal = safe_literal_impl<
typename utility::unsigned_stored_type<N, N>,
N,
P,
E
>;
template<
class T,
T N,
class P = void,
class E = void,
typename std::enable_if<
std::is_signed<T>::value,
int
>::type = 0
>
constexpr auto make_safe_literal_impl() {
return boost::safe_numerics::safe_signed_literal<N, P, E>();
}
template<
class T,
T N,
class P = void,
class E = void,
typename std::enable_if<
! std::is_signed<T>::value,
int
>::type = 0
>
constexpr auto make_safe_literal_impl() {
return boost::safe_numerics::safe_unsigned_literal<N, P, E>();
}
} // safe_numerics
} // boost
#define make_safe_literal(n, P, E) \
boost::safe_numerics::make_safe_literal_impl<decltype(n), n, P, E>()
/////////////////////////////////////////////////////////////////
// numeric limits for safe_literal etc.
#include <limits>
namespace std {
template<
typename T,
T N,
class P,
class E
>
class numeric_limits<boost::safe_numerics::safe_literal_impl<T, N, P, E> >
: public std::numeric_limits<T>
{
using SL = boost::safe_numerics::safe_literal_impl<T, N, P, E>;
public:
constexpr static SL lowest() noexcept {
return SL();
}
constexpr static SL min() noexcept {
return SL();
}
constexpr static SL max() noexcept {
return SL();
}
};
} // std
#endif // BOOST_NUMERIC_SAFE_INTEGER_LITERAL_HPP

View file

@ -0,0 +1,60 @@
#ifndef BOOST_NUMERIC_SAFE_INTEGER_RANGE_HPP
#define BOOST_NUMERIC_SAFE_INTEGER_RANGE_HPP
// Copyright (c) 2012 Robert Ramey
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <cstdint> // intmax_t, uintmax_t
#include "utility.hpp"
#include "safe_integer.hpp"
#include "native.hpp"
#include "exception_policies.hpp"
/////////////////////////////////////////////////////////////////
// higher level types implemented in terms of safe_base
namespace boost {
namespace safe_numerics {
/////////////////////////////////////////////////////////////////
// safe_signed_range
template <
std::intmax_t Min,
std::intmax_t Max,
class P = native,
class E = default_exception_policy
>
using safe_signed_range = safe_base<
typename utility::signed_stored_type<Min, Max>,
static_cast<typename utility::signed_stored_type<Min, Max> >(Min),
static_cast<typename utility::signed_stored_type<Min, Max> >(Max),
P,
E
>;
/////////////////////////////////////////////////////////////////
// safe_unsigned_range
template <
std::uintmax_t Min,
std::uintmax_t Max,
class P = native,
class E = default_exception_policy
>
using safe_unsigned_range = safe_base<
typename utility::unsigned_stored_type<Min, Max>,
static_cast<typename utility::unsigned_stored_type<Min, Max> >(Min),
static_cast<typename utility::unsigned_stored_type<Min, Max> >(Max),
P,
E
>;
} // safe_numerics
} // boost
#endif // BOOST_NUMERIC_SAFE_RANGE_HPP

View file

@ -0,0 +1,280 @@
#ifndef BOOST_NUMERIC_UTILITY_HPP
#define BOOST_NUMERIC_UTILITY_HPP
// Copyright (c) 2015 Robert Ramey
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <cstdint> // intmax_t, uintmax_t, uint8_t, ...
#include <algorithm>
#include <type_traits> // conditional
#include <limits>
#include <cassert>
#include <utility> // pair
#include <boost/integer.hpp> // (u)int_t<>::least, exact
namespace boost {
namespace safe_numerics {
namespace utility {
///////////////////////////////////////////////////////////////////////////////
// used for debugging
// provokes warning message with names of type T
// usage - print_types<T, ...>;
// see https://cukic.co/2019/02/19/tmp-testing-and-debugging-templates
/*
template<typename Tx>
using print_type = typename Tx::error_message;
*/
template <typename... Ts>
struct [[deprecated]] print_types {};
// display value of constexpr during compilation
// usage print_value(N) pn;
template<int N>
struct print_value
{
enum test : char {
value = N < 0 ? N - 256 : N + 256
};
};
#if 0
// static warning - same as static_assert but doesn't
// stop compilation.
template <typename T>
struct static_test{};
template <>
struct static_test<std::false_type>{
[[deprecated]] static_test(){}
};
template<typename T>
constexpr void static_warning(const T){
//using x = static_test<T>;
const static_test<T> x;
}
#endif
/*
// can be called by constexpr to produce a compile time
// trap of parameter passed is false.
// usage constexpr_assert(bool)
constexpr int constexpr_assert(const bool tf){
return 1 / tf;
}
*/
///////////////////////////////////////////////////////////////////////////////
// return an integral constant equal to the the number of bits
// held by some integer type (including the sign bit)
template<typename T>
using bits_type = std::integral_constant<
int,
std::numeric_limits<T>::digits
+ (std::numeric_limits<T>::is_signed ? 1 : 0)
>;
/*
From http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogObvious
Find the log base 2 of an integer with a lookup table
static const char LogTable256[256] =
{
#define LT(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n
-1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
LT(4), LT(5), LT(5), LT(6), LT(6), LT(6), LT(6),
LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7)
};
unsigned int v; // 32-bit word to find the log of
unsigned r; // r will be lg(v)
register unsigned int t, tt; // temporaries
if (tt = v >> 16)
{
r = (t = tt >> 8) ? 24 + LogTable256[t] : 16 + LogTable256[tt];
}
else
{
r = (t = v >> 8) ? 8 + LogTable256[t] : LogTable256[v];
}
The lookup table method takes only about 7 operations to find the log of a 32-bit value.
If extended for 64-bit quantities, it would take roughly 9 operations. Another operation
can be trimmed off by using four tables, with the possible additions incorporated into each.
Using int table elements may be faster, depending on your architecture.
*/
namespace ilog2_detail {
// I've "improved" the above and recast as C++ code which depends upon
// the optimizer to minimize the operations. This should result in
// nine operations to calculate the position of the highest order
// bit in a 64 bit number. RR
constexpr static unsigned int ilog2(const boost::uint_t<8>::exact & t){
#define LT(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n
const char LogTable256[256] = {
static_cast<const char>(-1), 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
LT(4), LT(5), LT(5), LT(6), LT(6), LT(6), LT(6),
LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7), LT(7)
};
return LogTable256[t];
}
constexpr static unsigned int ilog2(const boost::uint_t<16>::exact & t){
const boost::uint_t<8>::exact upper_half = (t >> 8);
return upper_half == 0
? ilog2(static_cast<boost::uint_t<8>::exact>(t))
: 8 + ilog2(upper_half);
}
constexpr static unsigned int ilog2(const boost::uint_t<32>::exact & t){
const boost::uint_t<16>::exact upper_half = (t >> 16);
return upper_half == 0
? ilog2(static_cast<boost::uint_t<16>::exact>(t))
: 16 + ilog2(upper_half);
}
constexpr static unsigned int ilog2(const boost::uint_t<64>::exact & t){
const boost::uint_t<32>::exact upper_half = (t >> 32);
return upper_half == 0
? ilog2(static_cast<boost::uint_t<32>::exact>(t))
: 32 + ilog2(upper_half);
}
} // ilog2_detail
template<typename T>
constexpr unsigned int ilog2(const T & t){
// log not defined for negative numbers
// assert(t > 0);
if(t == 0)
return 0;
return ilog2_detail::ilog2(
static_cast<
typename boost::uint_t<
bits_type<T>::value
>::least
>(t)
);
}
// the number of bits required to render the value in x
// including sign bit
template<typename T>
constexpr unsigned int significant_bits(const T & t){
return 1 + ((t < 0) ? ilog2(~t) : ilog2(t));
}
/*
// give the value t, return the number which corresponds
// to all 1's which is higher than that number
template<typename T>
constexpr unsigned int bits_value(const T & t){
const unsigned int sb = significant_bits(t);
const unsigned int sb_max = significant_bits(std::numeric_limits<T>::max());
return sb < sb_max ? ((sb << 1) - 1) : std::numeric_limits<T>::max();
}
*/
///////////////////////////////////////////////////////////////////////////////
// meta functions returning types
// If we use std::max in here we get internal compiler errors
// with MSVC (tested VC2017) ...
// Notes from https://en.cppreference.com/w/cpp/algorithm/max
// Capturing the result of std::max by reference if one of the parameters
// is rvalue produces a dangling reference if that parameter is returned.
template <class T>
// turns out this problem crashes all versions of gcc compilers. So
// make sure we return by value
//constexpr const T & max(
constexpr T max(
const T & lhs,
const T & rhs
){
return lhs > rhs ? lhs : rhs;
}
// given a signed range, return type required to hold all the values
// in the range
template<
std::intmax_t Min,
std::intmax_t Max
>
using signed_stored_type = typename boost::int_t<
max(
significant_bits(Min),
significant_bits(Max)
) + 1
>::least ;
// given an unsigned range, return type required to hold all the values
// in the range
template<
std::uintmax_t Min,
std::uintmax_t Max
>
// unsigned range
using unsigned_stored_type = typename boost::uint_t<
significant_bits(Max)
>::least;
///////////////////////////////////////////////////////////////////////////////
// constexpr functions
// need our own version because official version
// a) is not constexpr
// b) is not guarenteed to handle non-assignable types
template<typename T>
constexpr std::pair<T, T>
minmax(const std::initializer_list<T> l){
assert(l.size() > 0);
const T * minimum = l.begin();
const T * maximum = l.begin();
for(const T * i = l.begin(); i != l.end(); ++i){
if(*i < * minimum)
minimum = i;
else
if(* maximum < *i)
maximum = i;
}
return std::pair<T, T>{* minimum, * maximum};
}
// for any given t
// a) figure number of significant bits
// b) return a value with all significant bits set
// so for example:
// 3 == round_out(2) because
// 2 == 10 and 3 == 11
template<typename T>
constexpr T round_out(const T & t){
if(t >= 0){
const std::uint8_t sb = utility::significant_bits(t);
return (sb < sizeof(T) * 8)
? ((T)1 << sb) - 1
: std::numeric_limits<T>::max();
}
else{
const std::uint8_t sb = utility::significant_bits(~t);
return (sb < sizeof(T) * 8)
? ~(((T)1 << sb) - 1)
: std::numeric_limits<T>::min();
}
}
} // utility
} // safe_numerics
} // boost
#endif // BOOST_NUMERIC_UTILITY_HPP