123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235 |
- #ifndef BOOST_NUMERIC_EXCEPTION_POLICIES_HPP
- #define BOOST_NUMERIC_EXCEPTION_POLICIES_HPP
- // Copyright (c) 2015 Robert Ramey
- //
- // Distributed under 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)
- #include <boost/mp11.hpp>
- #include <boost/config.hpp> // BOOST_NO_EXCEPTIONS
- #include "exception.hpp"
- namespace boost {
- namespace safe_numerics {
- template<
- typename AE,
- typename IDB,
- typename UB,
- typename UV
- >
- struct exception_policy {
- constexpr static void on_arithmetic_error(
- const safe_numerics_error & e,
- const char * msg
- ){
- AE()(e, msg);
- }
- constexpr static void on_implementation_defined_behavior(
- const safe_numerics_error & e,
- const char * msg
- ){
- IDB()(e, msg);
- }
- constexpr static void on_undefined_behavior(
- const safe_numerics_error & e,
- const char * msg
- ){
- UB()(e, msg);
- }
- constexpr static void on_uninitialized_value(
- const safe_numerics_error & e,
- const char * msg
- ){
- UV()(e, msg);
- }
- };
- ////////////////////////////////////////////////////////////////////////////////
- // pre-made error action handers
- // ignore any error and just return.
- struct ignore_exception {
- constexpr ignore_exception() = default;
- constexpr void operator () (
- const boost::safe_numerics::safe_numerics_error &,
- const char *
- ){}
- };
- // emit compile time error if this is invoked.
- struct trap_exception {
- constexpr trap_exception() = default;
- // error will occur on operator call.
- // hopefully this will display arguments
- };
- // If an exceptional condition is detected at runtime throw the exception.
- struct throw_exception {
- constexpr throw_exception() = default;
- #ifndef BOOST_NO_EXCEPTIONS
- void operator()(
- const safe_numerics_error & e,
- const char * message
- ){
- throw std::system_error(std::error_code(e), message);
- }
- #else
- constexpr trap_exception()(const safe_numerics_error & e, const char * message);
- #endif
- };
- // given an error code - return the action code which it corresponds to.
- constexpr inline safe_numerics_actions
- make_safe_numerics_action(const safe_numerics_error & e){
- // we can't use standard algorithms since we want this to be constexpr
- // this brute force solution is simple and pretty fast anyway
- switch(e){
- case safe_numerics_error::negative_overflow_error:
- case safe_numerics_error::underflow_error:
- case safe_numerics_error::range_error:
- case safe_numerics_error::domain_error:
- case safe_numerics_error::positive_overflow_error:
- case safe_numerics_error::precision_overflow_error:
- return safe_numerics_actions::arithmetic_error;
- case safe_numerics_error::negative_value_shift:
- case safe_numerics_error::negative_shift:
- case safe_numerics_error::shift_too_large:
- return safe_numerics_actions::implementation_defined_behavior;
- case safe_numerics_error::uninitialized_value:
- return safe_numerics_actions::uninitialized_value;
- case safe_numerics_error::success:
- return safe_numerics_actions::no_action;
- default:
- assert(false);
- }
- // should never arrive here
- //include to suppress bogus warning
- return safe_numerics_actions::no_action;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // compile time error dispatcher
- // note slightly baroque implementation of a compile time switch statement
- // which instatiates only those cases which are actually invoked. This is
- // motivated to implement the "trap" functionality which will generate a syntax
- // error if and only a function which might fail is called.
- namespace dispatch_switch {
- template<class EP, safe_numerics_actions>
- struct dispatch_case {};
- template<class EP>
- struct dispatch_case<EP, safe_numerics_actions::uninitialized_value> {
- constexpr static void invoke(const safe_numerics_error & e, const char * msg){
- EP::on_uninitialized_value(e, msg);
- }
- };
- template<class EP>
- struct dispatch_case<EP, safe_numerics_actions::arithmetic_error> {
- constexpr static void invoke(const safe_numerics_error & e, const char * msg){
- EP::on_arithmetic_error(e, msg);
- }
- };
- template<class EP>
- struct dispatch_case<EP, safe_numerics_actions::implementation_defined_behavior> {
- constexpr static void invoke(const safe_numerics_error & e, const char * msg){
- EP::on_implementation_defined_behavior(e, msg);
- }
- };
- template<class EP>
- struct dispatch_case<EP, safe_numerics_actions::undefined_behavior> {
- constexpr static void invoke(const safe_numerics_error & e, const char * msg){
- EP::on_undefined_behavior(e, msg);
- }
- };
- } // dispatch_switch
- template<class EP, safe_numerics_error E>
- constexpr inline void
- dispatch(const char * msg){
- constexpr safe_numerics_actions a = make_safe_numerics_action(E);
- dispatch_switch::dispatch_case<EP, a>::invoke(E, msg);
- }
- template<class EP, class R>
- class dispatch_and_return {
- public:
- template<safe_numerics_error E>
- constexpr static checked_result<R> invoke(
- char const * const & msg
- ) {
- dispatch<EP, E>(msg);
- return checked_result<R>(E, msg);
- }
- };
- ////////////////////////////////////////////////////////////////////////////////
- // pre-made error policy classes
- // loose exception
- // - throw on arithmetic errors
- // - ignore other errors.
- // Some applications ignore these issues and still work and we don't
- // want to update them.
- using loose_exception_policy = exception_policy<
- throw_exception, // arithmetic error
- ignore_exception, // implementation defined behavior
- ignore_exception, // undefined behavior
- ignore_exception // uninitialized value
- >;
- // loose trap
- // same as above in that it doesn't check for various undefined behaviors
- // but traps at compile time for hard arithmetic errors. This policy
- // would be suitable for older embedded systems which depend on
- // bit manipulation operations to work.
- using loose_trap_policy = exception_policy<
- trap_exception, // arithmetic error
- ignore_exception, // implementation defined behavior
- ignore_exception, // undefined behavior
- ignore_exception // uninitialized value
- >;
- // strict exception
- // - throw at runtime on any kind of error
- // recommended for new code. Check everything at compile time
- // if possible and runtime if necessary. Trap or Throw as
- // appropriate. Should guarantee code to be portable across
- // architectures.
- using strict_exception_policy = exception_policy<
- throw_exception,
- throw_exception,
- throw_exception,
- ignore_exception
- >;
- // strict trap
- // Same as above but requires code to be written in such a way as to
- // make it impossible for errors to occur. This naturally will require
- // extra coding effort but might be justified for embedded and/or
- // safety critical systems.
- using strict_trap_policy = exception_policy<
- trap_exception,
- trap_exception,
- trap_exception,
- trap_exception
- >;
- // default policy
- // One would use this first. After experimentation, one might
- // replace some actions with ignore_exception
- using default_exception_policy = strict_exception_policy;
- } // namespace safe_numerics
- } // namespace boost
- #endif // BOOST_NUMERIC_EXCEPTION_POLICIES_HPP
|