]>
Commit | Line | Data |
---|---|---|
92f5a8d4 TL |
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> | |
20effc67 | 11 | #include <type_traits> // is_integral, enable_if, conditional is_convertible |
92f5a8d4 TL |
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 | template< | |
111 | class Stored, | |
112 | Stored Min, | |
113 | Stored Max, | |
114 | class P, // promotion polic | |
115 | class E // exception policy | |
116 | > | |
117 | class safe_base { | |
118 | private: | |
119 | BOOST_CONCEPT_ASSERT((PromotionPolicy<P>)); | |
120 | BOOST_CONCEPT_ASSERT((ExceptionPolicy<E>)); | |
121 | Stored m_t; | |
122 | ||
123 | template< | |
124 | class StoredX, | |
125 | StoredX MinX, | |
126 | StoredX MaxX, | |
127 | class PX, // promotion polic | |
128 | class EX // exception policy | |
129 | > | |
130 | friend class safe_base; | |
131 | ||
132 | friend class std::numeric_limits<safe_base>; | |
133 | ||
134 | template<class T> | |
135 | constexpr Stored validated_cast(const T & t) const; | |
136 | ||
92f5a8d4 TL |
137 | // stream support |
138 | ||
139 | template<class CharT, class Traits> | |
140 | void output(std::basic_ostream<CharT, Traits> & os) const; | |
141 | ||
142 | // note usage of friend declaration to mark function as | |
143 | // a global function rather than a member function. If | |
144 | // this is not done, the compiler will confuse this with | |
145 | // a member operator overload on the << operator. Weird | |
146 | // I know. But it's documented here | |
147 | // http://en.cppreference.com/w/cpp/language/friend | |
148 | // under the heading "Template friend operators" | |
149 | template<class CharT, class Traits> | |
150 | friend std::basic_ostream<CharT, Traits> & | |
151 | operator<<( | |
152 | std::basic_ostream<CharT, Traits> & os, | |
153 | const safe_base & t | |
154 | ){ | |
155 | t.output(os); | |
156 | return os; | |
157 | } | |
158 | ||
159 | template<class CharT, class Traits> | |
160 | void input(std::basic_istream<CharT, Traits> & is); | |
161 | ||
162 | // see above | |
163 | template<class CharT, class Traits> | |
164 | friend inline std::basic_istream<CharT, Traits> & | |
165 | operator>>( | |
166 | std::basic_istream<CharT, Traits> & is, | |
167 | safe_base & t | |
168 | ){ | |
169 | t.input(is); | |
170 | return is; | |
171 | } | |
172 | ||
173 | public: | |
174 | //////////////////////////////////////////////////////////// | |
175 | // constructors | |
176 | ||
92f5a8d4 TL |
177 | constexpr safe_base(); |
178 | ||
20effc67 | 179 | struct skip_validation{}; |
92f5a8d4 | 180 | |
20effc67 | 181 | constexpr explicit safe_base(const Stored & rhs, skip_validation); |
92f5a8d4 | 182 | |
20effc67 TL |
183 | // construct an instance of a safe type from an instance of a convertible underlying type. |
184 | template< | |
185 | class T, | |
92f5a8d4 | 186 | typename std::enable_if< |
20effc67 | 187 | std::is_convertible<T, Stored>::value, |
92f5a8d4 | 188 | bool |
20effc67 TL |
189 | >::type = 0 |
190 | > | |
191 | constexpr /*explicit*/ safe_base(const T & t); | |
92f5a8d4 | 192 | |
20effc67 TL |
193 | // construct an instance of a safe type from a literal value |
194 | template<typename T, T N, class Px, class Ex> | |
195 | constexpr /*explicit*/ safe_base(const safe_literal_impl<T, N, Px, Ex> & t); | |
92f5a8d4 TL |
196 | |
197 | // note: Rule of Five. Supply all or none of the following | |
198 | // a) user-defined destructor | |
199 | ~safe_base() = default; | |
200 | // b) copy-constructor | |
201 | constexpr safe_base(const safe_base &) = default; | |
202 | // c) copy-assignment | |
203 | constexpr safe_base & operator=(const safe_base &) = default; | |
204 | // d) move constructor | |
205 | constexpr safe_base(safe_base &&) = default; | |
206 | // e) move assignment operator | |
207 | constexpr safe_base & operator=(safe_base &&) = default; | |
208 | ||
209 | ///////////////////////////////////////////////////////////////// | |
210 | // casting operators for intrinsic integers | |
211 | // convert to any type which is not safe. safe types need to be | |
212 | // excluded to prevent ambiguous function selection which | |
213 | // would otherwise occur. validity of safe types is checked in | |
214 | // the constructor of safe types | |
215 | template< | |
216 | class R, | |
217 | typename std::enable_if< | |
218 | ! boost::safe_numerics::is_safe<R>::value, | |
219 | int | |
220 | >::type = 0 | |
221 | > | |
222 | constexpr /*explicit*/ operator R () const; | |
223 | ||
92f5a8d4 TL |
224 | ///////////////////////////////////////////////////////////////// |
225 | // modification binary operators | |
226 | template<class T> | |
227 | constexpr safe_base & | |
228 | operator=(const T & rhs){ | |
229 | m_t = validated_cast(rhs); | |
230 | return *this; | |
231 | } | |
232 | ||
92f5a8d4 TL |
233 | // mutating unary operators |
234 | safe_base & operator++(){ // pre increment | |
235 | return *this = *this + 1; | |
236 | } | |
237 | safe_base & operator--(){ // pre decrement | |
238 | return *this = *this - 1; | |
239 | } | |
240 | safe_base operator++(int){ // post increment | |
241 | safe_base old_t = *this; | |
242 | ++(*this); | |
243 | return old_t; | |
244 | } | |
245 | safe_base operator--(int){ // post decrement | |
246 | safe_base old_t = *this; | |
247 | --(*this); | |
248 | return old_t; | |
249 | } | |
250 | // non mutating unary operators | |
251 | constexpr auto operator+() const { // unary plus | |
252 | return *this; | |
253 | } | |
254 | // after much consideration, I've permited the resulting value of a unary | |
255 | // - to change the type. The C++ standard does invoke integral promotions | |
256 | // so it's changing the type as well. | |
257 | ||
258 | /* section 5.3.1 &8 of the C++ standard | |
259 | The operand of the unary - operator shall have arithmetic or unscoped | |
260 | enumeration type and the result is the negation of its operand. Integral | |
261 | promotion is performed on integral or enumeration operands. The negative | |
262 | of an unsigned quantity is computed by subtracting its value from 2n, | |
263 | where n is the number of bits in the promoted operand. The type of the | |
264 | result is the type of the promoted operand. | |
265 | */ | |
266 | constexpr auto operator-() const { // unary minus | |
267 | // if this is a unsigned type and the promotion policy is native | |
268 | // the result will be unsigned. But then the operation will fail | |
269 | // according to the requirements of arithmetic correctness. | |
270 | // if this is an unsigned type and the promotion policy is automatic. | |
271 | // the result will be signed. | |
272 | return 0 - *this; | |
273 | } | |
274 | /* section 5.3.1 &10 of the C++ standard | |
275 | The operand of ~ shall have integral or unscoped enumeration type; | |
276 | the result is the ones’ complement of its operand. Integral promotions | |
277 | are performed. The type of the result is the type of the promoted operand. | |
278 | */ | |
279 | constexpr auto operator~() const { // complement | |
280 | return ~Stored(0u) ^ *this; | |
281 | } | |
282 | }; | |
283 | ||
284 | } // safe_numerics | |
285 | } // boost | |
286 | ||
287 | ///////////////////////////////////////////////////////////////// | |
288 | // numeric limits for safe<int> etc. | |
289 | ||
290 | #include <limits> | |
291 | ||
292 | namespace std { | |
293 | ||
294 | template< | |
295 | class T, | |
296 | T Min, | |
297 | T Max, | |
298 | class P, | |
299 | class E | |
300 | > | |
301 | class numeric_limits<boost::safe_numerics::safe_base<T, Min, Max, P, E> > | |
302 | : public std::numeric_limits<T> | |
303 | { | |
304 | using SB = boost::safe_numerics::safe_base<T, Min, Max, P, E>; | |
305 | public: | |
306 | constexpr static SB lowest() noexcept { | |
307 | return SB(Min, typename SB::skip_validation()); | |
308 | } | |
309 | constexpr static SB min() noexcept { | |
310 | return SB(Min, typename SB::skip_validation()); | |
311 | } | |
312 | constexpr static SB max() noexcept { | |
313 | return SB(Max, typename SB::skip_validation()); | |
314 | } | |
315 | }; | |
316 | ||
317 | } // std | |
318 | ||
319 | #if BOOST_CLANG==1 | |
320 | #pragma GCC diagnostic pop | |
321 | #endif | |
322 | ||
323 | #endif // BOOST_NUMERIC_SAFE_BASE_HPP |