]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // Copyright John Maddock 2007. |
2 | // Copyright Paul A. Bristow 2010 | |
3 | // Use, modification and distribution are subject to the | |
4 | // Boost Software License, Version 1.0. (See accompanying file | |
5 | // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
6 | ||
7 | // Note that this file contains quickbook mark-up as well as code | |
8 | // and comments, don't change any of the special comment mark-ups! | |
9 | ||
10 | #include <iostream> | |
11 | #include <boost/format.hpp> | |
12 | using std::cout; using std::endl; using std::cerr; | |
13 | ||
14 | //[policy_eg_9 | |
15 | ||
16 | /*` | |
17 | The previous example was all well and good, but the custom error handlers | |
18 | didn't really do much of any use. In this example we'll implement all | |
19 | the custom handlers and show how the information provided to them can be | |
20 | used to generate nice formatted error messages. | |
21 | ||
22 | Each error handler has the general form: | |
23 | ||
24 | template <class T> | |
25 | T user_``['error_type]``( | |
26 | const char* function, | |
27 | const char* message, | |
28 | const T& val); | |
29 | ||
30 | and accepts three arguments: | |
31 | ||
32 | [variablelist | |
33 | [[const char* function] | |
34 | [The name of the function that raised the error, this string | |
35 | contains one or more %1% format specifiers that should be | |
36 | replaced by the name of real type T, like float or double.]] | |
37 | [[const char* message] | |
38 | [A message associated with the error, normally this | |
39 | contains a %1% format specifier that should be replaced with | |
40 | the value of ['value]: however note that overflow and underflow messages | |
41 | do not contain this %1% specifier (since the value of ['value] is | |
42 | immaterial in these cases).]] | |
43 | [[const T& value] | |
44 | [The value that caused the error: either an argument to the function | |
45 | if this is a domain or pole error, the tentative result | |
46 | if this is a denorm or evaluation error, or zero or infinity for | |
47 | underflow or overflow errors.]] | |
48 | ] | |
49 | ||
50 | As before we'll include the headers we need first: | |
51 | ||
52 | */ | |
53 | ||
54 | #include <boost/math/special_functions.hpp> | |
55 | ||
56 | /*` | |
57 | Next we'll implement our own error handlers for each type of error, | |
58 | starting with domain errors: | |
59 | */ | |
60 | ||
61 | namespace boost{ namespace math{ | |
62 | namespace policies | |
63 | { | |
64 | ||
65 | template <class T> | |
66 | T user_domain_error(const char* function, const char* message, const T& val) | |
67 | { | |
68 | /*` | |
69 | We'll begin with a bit of defensive programming in case function or message are empty: | |
70 | */ | |
71 | if(function == 0) | |
72 | function = "Unknown function with arguments of type %1%"; | |
73 | if(message == 0) | |
74 | message = "Cause unknown with bad argument %1%"; | |
75 | /*` | |
76 | Next we'll format the name of the function with the name of type T, perhaps double: | |
77 | */ | |
78 | std::string msg("Error in function "); | |
79 | msg += (boost::format(function) % typeid(T).name()).str(); | |
80 | /*` | |
81 | Then likewise format the error message with the value of parameter /val/, | |
82 | making sure we output all the potentially significant digits of /val/: | |
83 | */ | |
84 | msg += ": \n"; | |
85 | int prec = 2 + (std::numeric_limits<T>::digits * 30103UL) / 100000UL; | |
86 | // int prec = std::numeric_limits<T>::max_digits10; // For C++0X Standard Library | |
87 | msg += (boost::format(message) % boost::io::group(std::setprecision(prec), val)).str(); | |
88 | /*` | |
89 | Now we just have to do something with the message, we could throw an | |
90 | exception, but for the purposes of this example we'll just dump the message | |
91 | to std::cerr: | |
92 | */ | |
93 | std::cerr << msg << std::endl; | |
94 | /*` | |
95 | Finally the only sensible value we can return from a domain error is a NaN: | |
96 | */ | |
97 | return std::numeric_limits<T>::quiet_NaN(); | |
98 | } | |
99 | ||
100 | /*` | |
101 | Pole errors are essentially a special case of domain errors, | |
102 | so in this example we'll just return the result of a domain error: | |
103 | */ | |
104 | ||
105 | template <class T> | |
106 | T user_pole_error(const char* function, const char* message, const T& val) | |
107 | { | |
108 | return user_domain_error(function, message, val); | |
109 | } | |
110 | ||
111 | /*` | |
112 | Overflow errors are very similar to domain errors, except that there's | |
113 | no %1% format specifier in the /message/ parameter: | |
114 | */ | |
115 | template <class T> | |
116 | T user_overflow_error(const char* function, const char* message, const T& val) | |
117 | { | |
118 | if(function == 0) | |
119 | function = "Unknown function with arguments of type %1%"; | |
120 | if(message == 0) | |
121 | message = "Result of function is too large to represent"; | |
122 | ||
123 | std::string msg("Error in function "); | |
124 | msg += (boost::format(function) % typeid(T).name()).str(); | |
125 | ||
126 | msg += ": \n"; | |
127 | msg += message; | |
128 | ||
129 | std::cerr << msg << std::endl; | |
130 | ||
131 | // Value passed to the function is an infinity, just return it: | |
132 | return val; | |
133 | } | |
134 | ||
135 | /*` | |
136 | Underflow errors are much the same as overflow: | |
137 | */ | |
138 | ||
139 | template <class T> | |
140 | T user_underflow_error(const char* function, const char* message, const T& val) | |
141 | { | |
142 | if(function == 0) | |
143 | function = "Unknown function with arguments of type %1%"; | |
144 | if(message == 0) | |
145 | message = "Result of function is too small to represent"; | |
146 | ||
147 | std::string msg("Error in function "); | |
148 | msg += (boost::format(function) % typeid(T).name()).str(); | |
149 | ||
150 | msg += ": \n"; | |
151 | msg += message; | |
152 | ||
153 | std::cerr << msg << std::endl; | |
154 | ||
155 | // Value passed to the function is zero, just return it: | |
156 | return val; | |
157 | } | |
158 | ||
159 | /*` | |
160 | Denormalised results are much the same as underflow: | |
161 | */ | |
162 | ||
163 | template <class T> | |
164 | T user_denorm_error(const char* function, const char* message, const T& val) | |
165 | { | |
166 | if(function == 0) | |
167 | function = "Unknown function with arguments of type %1%"; | |
168 | if(message == 0) | |
169 | message = "Result of function is denormalised"; | |
170 | ||
171 | std::string msg("Error in function "); | |
172 | msg += (boost::format(function) % typeid(T).name()).str(); | |
173 | ||
174 | msg += ": \n"; | |
175 | msg += message; | |
176 | ||
177 | std::cerr << msg << std::endl; | |
178 | ||
179 | // Value passed to the function is denormalised, just return it: | |
180 | return val; | |
181 | } | |
182 | ||
183 | /*` | |
184 | Which leaves us with evaluation errors: these occur when an internal | |
185 | error occurs that prevents the function being fully evaluated. | |
186 | The parameter /val/ contains the closest approximation to the result | |
187 | found so far: | |
188 | */ | |
189 | ||
190 | template <class T> | |
191 | T user_evaluation_error(const char* function, const char* message, const T& val) | |
192 | { | |
193 | if(function == 0) | |
194 | function = "Unknown function with arguments of type %1%"; | |
195 | if(message == 0) | |
196 | message = "An internal evaluation error occurred with " | |
197 | "the best value calculated so far of %1%"; | |
198 | ||
199 | std::string msg("Error in function "); | |
200 | msg += (boost::format(function) % typeid(T).name()).str(); | |
201 | ||
202 | msg += ": \n"; | |
203 | int prec = 2 + (std::numeric_limits<T>::digits * 30103UL) / 100000UL; | |
204 | // int prec = std::numeric_limits<T>::max_digits10; // For C++0X Standard Library | |
205 | msg += (boost::format(message) % boost::io::group(std::setprecision(prec), val)).str(); | |
206 | ||
207 | std::cerr << msg << std::endl; | |
208 | ||
209 | // What do we return here? This is generally a fatal error, that should never occur, | |
210 | // so we just return a NaN for the purposes of the example: | |
211 | return std::numeric_limits<T>::quiet_NaN(); | |
212 | } | |
213 | ||
214 | } // policies | |
215 | }} // boost::math | |
216 | ||
217 | ||
218 | /*` | |
219 | Now we'll need to define a suitable policy that will call these handlers, | |
220 | and define some forwarding functions that make use of the policy: | |
221 | */ | |
222 | ||
223 | namespace mymath | |
224 | { // unnamed. | |
225 | ||
226 | using namespace boost::math::policies; | |
227 | ||
228 | typedef policy< | |
229 | domain_error<user_error>, | |
230 | pole_error<user_error>, | |
231 | overflow_error<user_error>, | |
232 | underflow_error<user_error>, | |
233 | denorm_error<user_error>, | |
234 | evaluation_error<user_error> | |
235 | > user_error_policy; | |
236 | ||
237 | BOOST_MATH_DECLARE_SPECIAL_FUNCTIONS(user_error_policy) | |
238 | ||
239 | } // unnamed namespace | |
240 | ||
241 | /*` | |
242 | We now have a set of forwarding functions, defined in namespace mymath, | |
243 | that all look something like this: | |
244 | ||
245 | `` | |
246 | template <class RealType> | |
247 | inline typename boost::math::tools::promote_args<RT>::type | |
248 | tgamma(RT z) | |
249 | { | |
250 | return boost::math::tgamma(z, user_error_policy()); | |
251 | } | |
252 | `` | |
253 | ||
254 | So that when we call `mymath::tgamma(z)` we really end up calling | |
255 | `boost::math::tgamma(z, user_error_policy())`, and any | |
256 | errors will get directed to our own error handlers: | |
257 | */ | |
258 | ||
259 | int main() | |
260 | { | |
261 | // Raise a domain error: | |
262 | cout << "Result of erf_inv(-10) is: " | |
263 | << mymath::erf_inv(-10) << std::endl << endl; | |
264 | // Raise a pole error: | |
265 | cout << "Result of tgamma(-10) is: " | |
266 | << mymath::tgamma(-10) << std::endl << endl; | |
267 | // Raise an overflow error: | |
268 | cout << "Result of tgamma(3000) is: " | |
269 | << mymath::tgamma(3000) << std::endl << endl; | |
270 | // Raise an underflow error: | |
271 | cout << "Result of tgamma(-190.5) is: " | |
272 | << mymath::tgamma(-190.5) << std::endl << endl; | |
f67539c2 | 273 | // Unfortunately we can't predictably raise a denormalised |
7c673cae FG |
274 | // result, nor can we raise an evaluation error in this example |
275 | // since these should never really occur! | |
276 | } // int main() | |
277 | ||
278 | /*` | |
279 | ||
280 | Which outputs: | |
281 | ||
282 | [pre | |
283 | Error in function boost::math::erf_inv<double>(double, double): | |
284 | Argument outside range \[-1, 1\] in inverse erf function (got p=-10). | |
285 | Result of erf_inv(-10) is: 1.#QNAN | |
286 | ||
287 | Error in function boost::math::tgamma<long double>(long double): | |
288 | Evaluation of tgamma at a negative integer -10. | |
289 | Result of tgamma(-10) is: 1.#QNAN | |
290 | ||
291 | Error in function boost::math::tgamma<long double>(long double): | |
292 | Result of tgamma is too large to represent. | |
293 | Error in function boost::math::tgamma<double>(double): | |
294 | Result of function is too large to represent | |
295 | Result of tgamma(3000) is: 1.#INF | |
296 | ||
297 | Error in function boost::math::tgamma<long double>(long double): | |
298 | Result of tgamma is too large to represent. | |
299 | Error in function boost::math::tgamma<long double>(long double): | |
300 | Result of tgamma is too small to represent. | |
301 | Result of tgamma(-190.5) is: 0 | |
302 | ] | |
303 | ||
304 | Notice how some of the calls result in an error handler being called more | |
305 | than once, or for more than one handler to be called: this is an artefact | |
306 | of the fact that many functions are implemented in terms of one or more | |
307 | sub-routines each of which may have it's own error handling. For example | |
308 | `tgamma(-190.5)` is implemented in terms of `tgamma(190.5)` - which overflows - | |
309 | the reflection formula for `tgamma` then notices that it is dividing by | |
310 | infinity and so underflows. | |
311 | */ | |
312 | ||
313 | //] //[/policy_eg_9] |