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>
user_domain_error(const char * function,const char * message,const T & val)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>
user_pole_error(const char * function,const char * message,const T & val)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>
user_overflow_error(const char * function,const char * message,const T & val)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>
user_underflow_error(const char * function,const char * message,const T & val)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>
user_denorm_error(const char * function,const char * message,const T & val)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>
user_evaluation_error(const char * function,const char * message,const T & val)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
main()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;
273 // Unfortunately we can't predictably raise a denormalised
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]
314