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