• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #ifndef BOOST_NUMERIC_EXCEPTION
2 #define BOOST_NUMERIC_EXCEPTION
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 // contains error indicators for results of doing checked
11 // arithmetic on native C++ types
12 
13 #include <algorithm>
14 #include <system_error> // error_code, system_error
15 #include <string>
16 #include <cassert>
17 #include <cstdint> // std::uint8_t
18 
19 // Using the system_error code facility.  This facility is more complex
20 // than meets the eye.  To fully understand what out intent here is,
21 // review http://blog.think-async.com/2010/04/system-error-support-in-c0x-part-5.html
22 // "Giving context-specific meaning to generic error codes"
23 
24 namespace boost {
25 namespace safe_numerics {
26 
27 // errors codes for safe numerics
28 
29 // in spite of the similarity, this list is distinct from the exceptions
30 // listed in documentation for std::exception.
31 
32 // note: Don't reorder these.  Code in the file checked_result_operations.hpp
33 // depends upon this order !!!
34 enum class safe_numerics_error : std::uint8_t {
35     success = 0,
36     positive_overflow_error,    // result is above representational maximum
37     negative_overflow_error,    // result is below representational minimum
38     domain_error,               // one operand is out of valid range
39     range_error,                // result cannot be produced for this operation
40     precision_overflow_error,   // result lost precision
41     underflow_error,            // result is too small to be represented
42     negative_value_shift,       // negative value in shift operator
43     negative_shift,             // shift a negative value
44     shift_too_large,            // l/r shift exceeds variable size
45     uninitialized_value         // creating of uninitialized value
46 };
47 
48 const std::uint8_t safe_numerics_casting_error_count =
49     static_cast<std::uint8_t>(safe_numerics_error::domain_error) + 1;
50 
51 const std::uint8_t safe_numerics_error_count =
52     static_cast<std::uint8_t>(safe_numerics_error::uninitialized_value) + 1;
53 
54 } // safe_numerics
55 } // boost
56 
57 namespace std {
58     template <>
59     struct is_error_code_enum<boost::safe_numerics::safe_numerics_error>
60         : public true_type {};
61 } // std
62 
63 namespace boost {
64 namespace safe_numerics {
65 
66 const class : public std::error_category {
67 public:
name() const68     virtual const char* name() const noexcept{
69         return "safe numerics error";
70     }
message(int ev) const71     virtual std::string message(int ev) const {
72         switch(static_cast<safe_numerics_error>(ev)){
73         case safe_numerics_error::success:
74             return "success";
75         case safe_numerics_error::positive_overflow_error:
76             return "positive overflow error";
77         case safe_numerics_error::negative_overflow_error:
78             return "negative overflow error";
79         case safe_numerics_error::underflow_error:
80             return "underflow error";
81         case safe_numerics_error::range_error:
82             return "range error";
83         case safe_numerics_error::precision_overflow_error:
84             return "precision_overflow_error";
85         case safe_numerics_error::domain_error:
86             return "domain error";
87         case safe_numerics_error::negative_shift:
88             return "negative shift";
89         case safe_numerics_error::negative_value_shift:
90             return "negative value shift";
91         case safe_numerics_error::shift_too_large:
92             return "shift too large";
93         case safe_numerics_error::uninitialized_value:
94             return "uninitialized value";
95         default:
96             assert(false);
97         }
98         return ""; // suppress bogus warning
99     }
100 } safe_numerics_error_category {};
101 
102 // constexpr - damn, can't use constexpr due to std::error_code
make_error_code(const safe_numerics_error & e)103 inline std::error_code make_error_code(const safe_numerics_error & e){
104     return std::error_code(static_cast<int>(e), safe_numerics_error_category);
105 }
106 
107 // actions for error_codes for safe numerics.  I've leveraged on
108 // error_condition in order to do this.  I'm not sure this is a good
109 // idea or not.
110 
111 enum class safe_numerics_actions {
112     no_action = 0,
113     uninitialized_value,
114     arithmetic_error,
115     implementation_defined_behavior,
116     undefined_behavior
117 };
118 
119 } // safe_numerics
120 } // boost
121 
122 namespace std {
123     template <>
124     struct is_error_condition_enum<boost::safe_numerics::safe_numerics_actions>
125         : public true_type {};
126 } // std
127 
128 namespace boost {
129 namespace safe_numerics {
130 
131 const class : public std::error_category {
132 public:
name() const133     virtual const char* name() const noexcept {
134         return "safe numerics error group";
135     }
message(int) const136     virtual std::string message(int) const {
137         return "safe numerics error group";
138     }
139     // return true if a given error code corresponds to a
140     // given safe numeric action
equivalent(const std::error_code & code,int condition) const141     virtual bool equivalent(
142         const std::error_code & code,
143         int condition
144     ) const noexcept {
145         if(code.category() != safe_numerics_error_category)
146             return false;
147         switch (static_cast<safe_numerics_actions>(condition)){
148         case safe_numerics_actions::no_action:
149             return code == safe_numerics_error::success;
150         case safe_numerics_actions::uninitialized_value:
151             return code == safe_numerics_error::uninitialized_value;
152         case safe_numerics_actions::arithmetic_error:
153             return code == safe_numerics_error::positive_overflow_error
154                 || code == safe_numerics_error::negative_overflow_error
155                 || code == safe_numerics_error::underflow_error
156                 || code == safe_numerics_error::range_error
157                 || code == safe_numerics_error::domain_error;
158         case safe_numerics_actions::implementation_defined_behavior:
159             return code == safe_numerics_error::negative_value_shift
160                 || code == safe_numerics_error::negative_shift
161                 || code == safe_numerics_error::shift_too_large;
162         case safe_numerics_actions::undefined_behavior:
163             return false;
164         default:
165             ;
166         }
167         // should never arrive here
168         assert(false);
169         // suppress bogus warning
170         return false;
171     }
172 } safe_numerics_actions_category {};
173 
174 // the following function is used to "finish" implementation of conversion
175 // of safe_numerics_error to std::error_condition.  At least for now, this
176 // isn't being used and defining here it can lead duplicate symbol errors
177 // depending on the compiler.  So suppress it until further notice
178 #if 0
179 std::error_condition make_error_condition(const safe_numerics_error & e) {
180     return std::error_condition(
181         static_cast<int>(e),
182         safe_numerics_error_category
183     );
184 }
185 #endif
186 
187 } // safe_numerics
188 } // boost
189 
190 #endif // BOOST_NUMERIC_CHECKED_RESULT
191