| 1 | #ifndef BOOST_NUMERIC_SAFE_BASE_HPP |
| 2 | #define BOOST_NUMERIC_SAFE_BASE_HPP |
| 3 | |
| 4 | // Copyright (c) 2012 Robert Ramey |
| 5 | // |
| 6 | // Distributed under the Boost Software License, Version 1.0. (See |
| 7 | // accompanying file LICENSE_1_0.txt or copy at |
| 8 | // http://www.boost.org/LICENSE_1_0.txt) |
| 9 | |
| 10 | #include <limits> |
| 11 | #include <type_traits> // is_integral, enable_if, conditional is_convertible |
| 12 | #include <boost/config.hpp> // BOOST_CLANG |
| 13 | #include "concept/exception_policy.hpp" |
| 14 | #include "concept/promotion_policy.hpp" |
| 15 | |
| 16 | #include "safe_common.hpp" |
| 17 | #include "exception_policies.hpp" |
| 18 | |
| 19 | #include "boost/concept/assert.hpp" |
| 20 | |
| 21 | namespace boost { |
| 22 | namespace safe_numerics { |
| 23 | |
| 24 | ///////////////////////////////////////////////////////////////// |
| 25 | // forward declarations to support friend function declarations |
| 26 | // in safe_base |
| 27 | |
| 28 | template< |
| 29 | class Stored, |
| 30 | Stored Min, |
| 31 | Stored Max, |
| 32 | class P, // promotion polic |
| 33 | class E // exception policy |
| 34 | > |
| 35 | class safe_base; |
| 36 | |
| 37 | template< |
| 38 | class T, |
| 39 | T Min, |
| 40 | T Max, |
| 41 | class P, |
| 42 | class E |
| 43 | > |
| 44 | struct is_safe<safe_base<T, Min, Max, P, E> > : public std::true_type |
| 45 | {}; |
| 46 | |
| 47 | template< |
| 48 | class T, |
| 49 | T Min, |
| 50 | T Max, |
| 51 | class P, |
| 52 | class E |
| 53 | > |
| 54 | struct get_promotion_policy<safe_base<T, Min, Max, P, E> > { |
| 55 | using type = P; |
| 56 | }; |
| 57 | |
| 58 | template< |
| 59 | class T, |
| 60 | T Min, |
| 61 | T Max, |
| 62 | class P, |
| 63 | class E |
| 64 | > |
| 65 | struct get_exception_policy<safe_base<T, Min, Max, P, E> > { |
| 66 | using type = E; |
| 67 | }; |
| 68 | |
| 69 | template< |
| 70 | class T, |
| 71 | T Min, |
| 72 | T Max, |
| 73 | class P, |
| 74 | class E |
| 75 | > |
| 76 | struct base_type<safe_base<T, Min, Max, P, E> > { |
| 77 | using type = T; |
| 78 | }; |
| 79 | |
| 80 | template< |
| 81 | class T, |
| 82 | T Min, |
| 83 | T Max, |
| 84 | class P, |
| 85 | class E |
| 86 | > |
| 87 | constexpr T base_value( |
| 88 | const safe_base<T, Min, Max, P, E> & st |
| 89 | ) { |
| 90 | return static_cast<T>(st); |
| 91 | } |
| 92 | |
| 93 | template< |
| 94 | typename T, |
| 95 | T N, |
| 96 | class P, // promotion policy |
| 97 | class E // exception policy |
| 98 | > |
| 99 | class safe_literal_impl; |
| 100 | |
| 101 | // works for both GCC and clang |
| 102 | #if BOOST_CLANG==1 |
| 103 | #pragma GCC diagnostic push |
| 104 | #pragma GCC diagnostic ignored "-Wmismatched-tags" |
| 105 | #endif |
| 106 | |
| 107 | ///////////////////////////////////////////////////////////////// |
| 108 | // Main implementation |
| 109 | |
| 110 | // note in the current version of the library, it is a requirement than type |
| 111 | // type "Stored" be a scalar type. Problem is when violates this rule, the error message (on clang) |
| 112 | // is "A non-type template parameter cannot have type ... " where ... is some non-scalar type |
| 113 | // The example which triggered this investigation is safe<safe<int>> . It was difficult to make |
| 114 | // the trip from the error message to the source of the problem, hence we're including this |
| 115 | // comment here. |
| 116 | template< |
| 117 | class Stored, |
| 118 | Stored Min, |
| 119 | Stored Max, |
| 120 | class P, // promotion polic |
| 121 | class E // exception policy |
| 122 | > |
| 123 | class safe_base { |
| 124 | private: |
| 125 | BOOST_CONCEPT_ASSERT((PromotionPolicy<P>)); |
| 126 | BOOST_CONCEPT_ASSERT((ExceptionPolicy<E>)); |
| 127 | Stored m_t; |
| 128 | |
| 129 | template<class T> |
| 130 | constexpr Stored validated_cast(const T & t) const; |
| 131 | |
| 132 | // stream support |
| 133 | |
| 134 | template<class CharT, class Traits> |
| 135 | void output(std::basic_ostream<CharT, Traits> & os) const; |
| 136 | |
| 137 | // note usage of friend declaration to mark function as |
| 138 | // a global function rather than a member function. If |
| 139 | // this is not done, the compiler will confuse this with |
| 140 | // a member operator overload on the << operator. Weird |
| 141 | // I know. But it's documented here |
| 142 | // http://en.cppreference.com/w/cpp/language/friend |
| 143 | // under the heading "Template friend operators" |
| 144 | template<class CharT, class Traits> |
| 145 | friend std::basic_ostream<CharT, Traits> & |
| 146 | operator<<( |
| 147 | std::basic_ostream<CharT, Traits> & os, |
| 148 | const safe_base & t |
| 149 | ){ |
| 150 | t.output(os); |
| 151 | return os; |
| 152 | } |
| 153 | |
| 154 | template<class CharT, class Traits> |
| 155 | void input(std::basic_istream<CharT, Traits> & is); |
| 156 | |
| 157 | // see above |
| 158 | template<class CharT, class Traits> |
| 159 | friend inline std::basic_istream<CharT, Traits> & |
| 160 | operator>>( |
| 161 | std::basic_istream<CharT, Traits> & is, |
| 162 | safe_base & t |
| 163 | ){ |
| 164 | t.input(is); |
| 165 | return is; |
| 166 | } |
| 167 | |
| 168 | public: |
| 169 | //////////////////////////////////////////////////////////// |
| 170 | // constructors |
| 171 | |
| 172 | constexpr safe_base(); |
| 173 | |
| 174 | struct skip_validation{}; |
| 175 | |
| 176 | constexpr explicit safe_base(const Stored & rhs, skip_validation); |
| 177 | |
| 178 | // construct an instance of a safe type from an instance of a convertible underlying type. |
| 179 | template< |
| 180 | class T, |
| 181 | typename std::enable_if< |
| 182 | std::is_convertible<T, Stored>::value, |
| 183 | bool |
| 184 | >::type = 0 |
| 185 | > |
| 186 | constexpr /*explicit*/ safe_base(const T & t); |
| 187 | |
| 188 | // construct an instance of a safe type from a literal value |
| 189 | template<typename T, T N, class Px, class Ex> |
| 190 | constexpr /*explicit*/ safe_base(const safe_literal_impl<T, N, Px, Ex> & t); |
| 191 | |
| 192 | // note: Rule of Five. Supply all or none of the following |
| 193 | // a) user-defined destructor |
| 194 | ~safe_base() = default; |
| 195 | // b) copy-constructor |
| 196 | constexpr safe_base(const safe_base &) = default; |
| 197 | // c) copy-assignment |
| 198 | constexpr safe_base & operator=(const safe_base &) = default; |
| 199 | // d) move constructor |
| 200 | constexpr safe_base(safe_base &&) = default; |
| 201 | // e) move assignment operator |
| 202 | constexpr safe_base & operator=(safe_base &&) = default; |
| 203 | |
| 204 | ///////////////////////////////////////////////////////////////// |
| 205 | // casting operators for intrinsic integers |
| 206 | // convert to any type which is not safe. safe types need to be |
| 207 | // excluded to prevent ambiguous function selection which |
| 208 | // would otherwise occur. validity of safe types is checked in |
| 209 | // the constructor of safe types |
| 210 | template< |
| 211 | class R, |
| 212 | typename std::enable_if< |
| 213 | ! boost::safe_numerics::is_safe<R>::value, |
| 214 | int |
| 215 | >::type = 0 |
| 216 | > |
| 217 | constexpr /*explicit*/ operator R () const; |
| 218 | |
| 219 | ///////////////////////////////////////////////////////////////// |
| 220 | // modification binary operators |
| 221 | template<class T> |
| 222 | constexpr safe_base & |
| 223 | operator=(const T & rhs){ |
| 224 | m_t = validated_cast(rhs); |
| 225 | return *this; |
| 226 | } |
| 227 | |
| 228 | // mutating unary operators |
| 229 | constexpr safe_base & operator++(){ // pre increment |
| 230 | return *this = *this + 1; |
| 231 | } |
| 232 | constexpr safe_base & operator--(){ // pre decrement |
| 233 | return *this = *this - 1; |
| 234 | } |
| 235 | constexpr safe_base operator++(int){ // post increment |
| 236 | safe_base old_t = *this; |
| 237 | ++(*this); |
| 238 | return old_t; |
| 239 | } |
| 240 | constexpr safe_base operator--(int){ // post decrement |
| 241 | safe_base old_t = *this; |
| 242 | --(*this); |
| 243 | return old_t; |
| 244 | } |
| 245 | // non mutating unary operators |
| 246 | constexpr auto operator+() const { // unary plus |
| 247 | return *this; |
| 248 | } |
| 249 | // after much consideration, I've permited the resulting value of a unary |
| 250 | // - to change the type. The C++ standard does invoke integral promotions |
| 251 | // so it's changing the type as well. |
| 252 | |
| 253 | /* section 5.3.1 &8 of the C++ standard |
| 254 | The operand of the unary - operator shall have arithmetic or unscoped |
| 255 | enumeration type and the result is the negation of its operand. Integral |
| 256 | promotion is performed on integral or enumeration operands. The negative |
| 257 | of an unsigned quantity is computed by subtracting its value from 2n, |
| 258 | where n is the number of bits in the promoted operand. The type of the |
| 259 | result is the type of the promoted operand. |
| 260 | */ |
| 261 | constexpr auto operator-() const { // unary minus |
| 262 | // if this is a unsigned type and the promotion policy is native |
| 263 | // the result will be unsigned. But then the operation will fail |
| 264 | // according to the requirements of arithmetic correctness. |
| 265 | // if this is an unsigned type and the promotion policy is automatic. |
| 266 | // the result will be signed. |
| 267 | return 0 - *this; |
| 268 | } |
| 269 | /* section 5.3.1 &10 of the C++ standard |
| 270 | The operand of ~ shall have integral or unscoped enumeration type; |
| 271 | the result is the ones’ complement of its operand. Integral promotions |
| 272 | are performed. The type of the result is the type of the promoted operand. |
| 273 | */ |
| 274 | constexpr auto operator~() const { // complement |
| 275 | return ~Stored(0u) ^ *this; |
| 276 | } |
| 277 | }; |
| 278 | |
| 279 | } // safe_numerics |
| 280 | } // boost |
| 281 | |
| 282 | ///////////////////////////////////////////////////////////////// |
| 283 | // numeric limits for safe<int> etc. |
| 284 | |
| 285 | #include <limits> |
| 286 | |
| 287 | namespace std { |
| 288 | |
| 289 | template< |
| 290 | class T, |
| 291 | T Min, |
| 292 | T Max, |
| 293 | class P, |
| 294 | class E |
| 295 | > |
| 296 | class numeric_limits<boost::safe_numerics::safe_base<T, Min, Max, P, E> > |
| 297 | : public std::numeric_limits<T> |
| 298 | { |
| 299 | using SB = boost::safe_numerics::safe_base<T, Min, Max, P, E>; |
| 300 | public: |
| 301 | constexpr static SB lowest() noexcept { |
| 302 | return SB(Min, typename SB::skip_validation()); |
| 303 | } |
| 304 | constexpr static SB min() noexcept { |
| 305 | return SB(Min, typename SB::skip_validation()); |
| 306 | } |
| 307 | constexpr static SB max() noexcept { |
| 308 | return SB(Max, typename SB::skip_validation()); |
| 309 | } |
| 310 | }; |
| 311 | |
| 312 | } // std |
| 313 | |
| 314 | #if BOOST_CLANG==1 |
| 315 | #pragma GCC diagnostic pop |
| 316 | #endif |
| 317 | |
| 318 | #endif // BOOST_NUMERIC_SAFE_BASE_HPP |
| 319 | |