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