mirror of
				https://github.com/saitohirga/WSJT-X.git
				synced 2025-10-31 13:10:19 -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]
 |