• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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