mirror of
				https://github.com/saitohirga/WSJT-X.git
				synced 2025-10-31 04:50:34 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			314 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			314 lines
		
	
	
		
			9.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| //  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]
 |