123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- // Copyright John Maddock 2007.
- // Copyright Paul A. Bristow 2010
- // Use, modification and distribution are subject to the
- // Boost Software License, Version 1.0. (See accompanying file
- // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- // Note that this file contains quickbook mark-up as well as code
- // and comments, don't change any of the special comment mark-ups!
- #include <iostream>
- #include <boost/format.hpp>
- using std::cout; using std::endl; using std::cerr;
- //[policy_eg_9
- /*`
- The previous example was all well and good, but the custom error handlers
- didn't really do much of any use. In this example we'll implement all
- the custom handlers and show how the information provided to them can be
- used to generate nice formatted error messages.
- Each error handler has the general form:
- template <class T>
- T user_``['error_type]``(
- const char* function,
- const char* message,
- const T& val);
- and accepts three arguments:
- [variablelist
- [[const char* function]
- [The name of the function that raised the error, this string
- contains one or more %1% format specifiers that should be
- replaced by the name of real type T, like float or double.]]
- [[const char* message]
- [A message associated with the error, normally this
- contains a %1% format specifier that should be replaced with
- the value of ['value]: however note that overflow and underflow messages
- do not contain this %1% specifier (since the value of ['value] is
- immaterial in these cases).]]
- [[const T& value]
- [The value that caused the error: either an argument to the function
- if this is a domain or pole error, the tentative result
- if this is a denorm or evaluation error, or zero or infinity for
- underflow or overflow errors.]]
- ]
- As before we'll include the headers we need first:
- */
- #include <boost/math/special_functions.hpp>
- /*`
- Next we'll implement our own error handlers for each type of error,
- starting with domain errors:
- */
- namespace boost{ namespace math{
- namespace policies
- {
- template <class T>
- T user_domain_error(const char* function, const char* message, const T& val)
- {
- /*`
- We'll begin with a bit of defensive programming in case function or message are empty:
- */
- if(function == 0)
- function = "Unknown function with arguments of type %1%";
- if(message == 0)
- message = "Cause unknown with bad argument %1%";
- /*`
- Next we'll format the name of the function with the name of type T, perhaps double:
- */
- std::string msg("Error in function ");
- msg += (boost::format(function) % typeid(T).name()).str();
- /*`
- Then likewise format the error message with the value of parameter /val/,
- making sure we output all the potentially significant digits of /val/:
- */
- msg += ": \n";
- int prec = 2 + (std::numeric_limits<T>::digits * 30103UL) / 100000UL;
- // int prec = std::numeric_limits<T>::max_digits10; // For C++0X Standard Library
- msg += (boost::format(message) % boost::io::group(std::setprecision(prec), val)).str();
- /*`
- Now we just have to do something with the message, we could throw an
- exception, but for the purposes of this example we'll just dump the message
- to std::cerr:
- */
- std::cerr << msg << std::endl;
- /*`
- Finally the only sensible value we can return from a domain error is a NaN:
- */
- return std::numeric_limits<T>::quiet_NaN();
- }
- /*`
- Pole errors are essentially a special case of domain errors,
- so in this example we'll just return the result of a domain error:
- */
- template <class T>
- T user_pole_error(const char* function, const char* message, const T& val)
- {
- return user_domain_error(function, message, val);
- }
- /*`
- Overflow errors are very similar to domain errors, except that there's
- no %1% format specifier in the /message/ parameter:
- */
- template <class T>
- T user_overflow_error(const char* function, const char* message, const T& val)
- {
- if(function == 0)
- function = "Unknown function with arguments of type %1%";
- if(message == 0)
- message = "Result of function is too large to represent";
- std::string msg("Error in function ");
- msg += (boost::format(function) % typeid(T).name()).str();
- msg += ": \n";
- msg += message;
- std::cerr << msg << std::endl;
- // Value passed to the function is an infinity, just return it:
- return val;
- }
- /*`
- Underflow errors are much the same as overflow:
- */
- template <class T>
- T user_underflow_error(const char* function, const char* message, const T& val)
- {
- if(function == 0)
- function = "Unknown function with arguments of type %1%";
- if(message == 0)
- message = "Result of function is too small to represent";
- std::string msg("Error in function ");
- msg += (boost::format(function) % typeid(T).name()).str();
- msg += ": \n";
- msg += message;
- std::cerr << msg << std::endl;
- // Value passed to the function is zero, just return it:
- return val;
- }
- /*`
- Denormalised results are much the same as underflow:
- */
- template <class T>
- T user_denorm_error(const char* function, const char* message, const T& val)
- {
- if(function == 0)
- function = "Unknown function with arguments of type %1%";
- if(message == 0)
- message = "Result of function is denormalised";
- std::string msg("Error in function ");
- msg += (boost::format(function) % typeid(T).name()).str();
- msg += ": \n";
- msg += message;
- std::cerr << msg << std::endl;
- // Value passed to the function is denormalised, just return it:
- return val;
- }
- /*`
- Which leaves us with evaluation errors: these occur when an internal
- error occurs that prevents the function being fully evaluated.
- The parameter /val/ contains the closest approximation to the result
- found so far:
- */
- template <class T>
- T user_evaluation_error(const char* function, const char* message, const T& val)
- {
- if(function == 0)
- function = "Unknown function with arguments of type %1%";
- if(message == 0)
- message = "An internal evaluation error occurred with "
- "the best value calculated so far of %1%";
- std::string msg("Error in function ");
- msg += (boost::format(function) % typeid(T).name()).str();
- msg += ": \n";
- int prec = 2 + (std::numeric_limits<T>::digits * 30103UL) / 100000UL;
- // int prec = std::numeric_limits<T>::max_digits10; // For C++0X Standard Library
- msg += (boost::format(message) % boost::io::group(std::setprecision(prec), val)).str();
- std::cerr << msg << std::endl;
- // What do we return here? This is generally a fatal error, that should never occur,
- // so we just return a NaN for the purposes of the example:
- return std::numeric_limits<T>::quiet_NaN();
- }
- } // policies
- }} // boost::math
- /*`
- Now we'll need to define a suitable policy that will call these handlers,
- and define some forwarding functions that make use of the policy:
- */
- namespace mymath
- { // unnamed.
- using namespace boost::math::policies;
- typedef policy<
- domain_error<user_error>,
- pole_error<user_error>,
- overflow_error<user_error>,
- underflow_error<user_error>,
- denorm_error<user_error>,
- evaluation_error<user_error>
- > user_error_policy;
- BOOST_MATH_DECLARE_SPECIAL_FUNCTIONS(user_error_policy)
- } // unnamed namespace
- /*`
- We now have a set of forwarding functions, defined in namespace mymath,
- that all look something like this:
- ``
- template <class RealType>
- inline typename boost::math::tools::promote_args<RT>::type
- tgamma(RT z)
- {
- return boost::math::tgamma(z, user_error_policy());
- }
- ``
- So that when we call `mymath::tgamma(z)` we really end up calling
- `boost::math::tgamma(z, user_error_policy())`, and any
- errors will get directed to our own error handlers:
- */
- int main()
- {
- // Raise a domain error:
- cout << "Result of erf_inv(-10) is: "
- << mymath::erf_inv(-10) << std::endl << endl;
- // Raise a pole error:
- cout << "Result of tgamma(-10) is: "
- << mymath::tgamma(-10) << std::endl << endl;
- // Raise an overflow error:
- cout << "Result of tgamma(3000) is: "
- << mymath::tgamma(3000) << std::endl << endl;
- // Raise an underflow error:
- cout << "Result of tgamma(-190.5) is: "
- << mymath::tgamma(-190.5) << std::endl << endl;
- // Unfortunately we can't predicably raise a denormalised
- // result, nor can we raise an evaluation error in this example
- // since these should never really occur!
- } // int main()
- /*`
- Which outputs:
- [pre
- Error in function boost::math::erf_inv<double>(double, double):
- Argument outside range \[-1, 1\] in inverse erf function (got p=-10).
- Result of erf_inv(-10) is: 1.#QNAN
- Error in function boost::math::tgamma<long double>(long double):
- Evaluation of tgamma at a negative integer -10.
- Result of tgamma(-10) is: 1.#QNAN
- Error in function boost::math::tgamma<long double>(long double):
- Result of tgamma is too large to represent.
- Error in function boost::math::tgamma<double>(double):
- Result of function is too large to represent
- Result of tgamma(3000) is: 1.#INF
- Error in function boost::math::tgamma<long double>(long double):
- Result of tgamma is too large to represent.
- Error in function boost::math::tgamma<long double>(long double):
- Result of tgamma is too small to represent.
- Result of tgamma(-190.5) is: 0
- ]
- Notice how some of the calls result in an error handler being called more
- than once, or for more than one handler to be called: this is an artefact
- of the fact that many functions are implemented in terms of one or more
- sub-routines each of which may have it's own error handling. For example
- `tgamma(-190.5)` is implemented in terms of `tgamma(190.5)` - which overflows -
- the reflection formula for `tgamma` then notices that it is dividing by
- infinity and so underflows.
- */
- //] //[/policy_eg_9]
|