mirror of
https://github.com/yuzu-emu/ext-boost.git
synced 2024-12-22 13:25:29 +00:00
ext-boost: Add safe-numerics.
This commit is contained in:
parent
1a1e0fc797
commit
caf8e19fb1
13
boost/safe_numerics/CMakeLists.txt
Normal file
13
boost/safe_numerics/CMakeLists.txt
Normal 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
|
||||
####################
|
479
boost/safe_numerics/automatic.hpp
Normal file
479
boost/safe_numerics/automatic.hpp
Normal 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
|
217
boost/safe_numerics/checked_default.hpp
Normal file
217
boost/safe_numerics/checked_default.hpp
Normal 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
|
214
boost/safe_numerics/checked_float.hpp
Normal file
214
boost/safe_numerics/checked_float.hpp
Normal 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
|
||||
|
819
boost/safe_numerics/checked_integer.hpp
Normal file
819
boost/safe_numerics/checked_integer.hpp
Normal 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
|
146
boost/safe_numerics/checked_result.hpp
Normal file
146
boost/safe_numerics/checked_result.hpp
Normal 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
|
1217
boost/safe_numerics/checked_result_operations.hpp
Normal file
1217
boost/safe_numerics/checked_result_operations.hpp
Normal file
File diff suppressed because it is too large
Load diff
14
boost/safe_numerics/concept/CMakeLists.txt
Normal file
14
boost/safe_numerics/concept/CMakeLists.txt
Normal 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
|
||||
####################
|
29
boost/safe_numerics/concept/exception_policy.hpp
Normal file
29
boost/safe_numerics/concept/exception_policy.hpp
Normal 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
|
27
boost/safe_numerics/concept/integer.hpp
Normal file
27
boost/safe_numerics/concept/integer.hpp
Normal 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
|
29
boost/safe_numerics/concept/numeric.hpp
Normal file
29
boost/safe_numerics/concept/numeric.hpp
Normal 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
|
33
boost/safe_numerics/concept/promotion_policy.hpp
Normal file
33
boost/safe_numerics/concept/promotion_policy.hpp
Normal 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
|
34
boost/safe_numerics/concept/safe_numeric.hpp
Normal file
34
boost/safe_numerics/concept/safe_numeric.hpp
Normal 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
197
boost/safe_numerics/cpp.hpp
Normal 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
|
190
boost/safe_numerics/exception.hpp
Normal file
190
boost/safe_numerics/exception.hpp
Normal 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
|
230
boost/safe_numerics/exception_policies.hpp
Normal file
230
boost/safe_numerics/exception_policies.hpp
Normal 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
|
310
boost/safe_numerics/interval.hpp
Normal file
310
boost/safe_numerics/interval.hpp
Normal 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
|
110
boost/safe_numerics/native.hpp
Normal file
110
boost/safe_numerics/native.hpp
Normal 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
|
71
boost/safe_numerics/range_value.hpp
Normal file
71
boost/safe_numerics/range_value.hpp
Normal 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
|
349
boost/safe_numerics/safe_base.hpp
Normal file
349
boost/safe_numerics/safe_base.hpp
Normal 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
|
1745
boost/safe_numerics/safe_base_operations.hpp
Normal file
1745
boost/safe_numerics/safe_base_operations.hpp
Normal file
File diff suppressed because it is too large
Load diff
44
boost/safe_numerics/safe_common.hpp
Normal file
44
boost/safe_numerics/safe_common.hpp
Normal 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
|
180
boost/safe_numerics/safe_compare.hpp
Normal file
180
boost/safe_numerics/safe_compare.hpp
Normal 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
|
42
boost/safe_numerics/safe_integer.hpp
Normal file
42
boost/safe_numerics/safe_integer.hpp
Normal 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
|
260
boost/safe_numerics/safe_integer_literal.hpp
Normal file
260
boost/safe_numerics/safe_integer_literal.hpp
Normal 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
|
60
boost/safe_numerics/safe_integer_range.hpp
Normal file
60
boost/safe_numerics/safe_integer_range.hpp
Normal 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
|
280
boost/safe_numerics/utility.hpp
Normal file
280
boost/safe_numerics/utility.hpp
Normal 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
|
Loading…
Reference in a new issue