diff --git a/CMake/CMakeLists.txt b/CMake/CMakeLists.txt index 7977945..8271db6 100644 --- a/CMake/CMakeLists.txt +++ b/CMake/CMakeLists.txt @@ -210,12 +210,12 @@ foreach(file_path ${example_list}) add_test(NAME ${base_name} COMMAND ${base_name}) endforeach(file_path) -set_target_properties(test_z example84 PROPERTIES +set_target_properties(test_z example81 example93 PROPERTIES EXCLUDE_FROM_ALL TRUE EXCLUDE_FROM_DEFAULT_BUILD TRUE ) -set_target_properties(example81 example91 example92 PROPERTIES +set_target_properties(example84 PROPERTIES WILL_FAIL TRUE ) diff --git a/examples/example84.cpp b/examples/example84.cpp index fc5d9a1..333318d 100644 --- a/examples/example84.cpp +++ b/examples/example84.cpp @@ -35,7 +35,7 @@ auto f(const safe_t & x, const safe_t & y){ } int main(int argc, const char * argv[]){ - std::cout << "example 83:\n"; + std::cout << "example 84:\n"; try{ input_safe_t x, y; std::cin >> x >> y; // read varibles, throw exception diff --git a/examples/example91.cpp b/examples/example91.cpp index fe7fc3b..3c7aa90 100644 --- a/examples/example91.cpp +++ b/examples/example91.cpp @@ -78,12 +78,13 @@ int main(){ } catch(std::exception & e){ std::cout << e.what() << '\n'; - return 1; + // we expect to trap an exception + return 0; } catch(...){ std::cout << "test interrupted\n"; return 1; } std::cout << "end test\n"; - return 0; + return 1; } \ No newline at end of file diff --git a/examples/example93.cpp b/examples/example93.cpp index c2ac329..fdadd1d 100644 --- a/examples/example93.cpp +++ b/examples/example93.cpp @@ -30,6 +30,8 @@ using pic16_promotion = boost::numeric::cpp< 32 // long long >; +using exception_policy = boost::numeric::trap_exception; + // define safe types used desktop version of the program. In conjunction // with the promotion policy above, this will permit us to guarantee that // the resulting program will be free of arithmetic errors introduced by @@ -38,32 +40,38 @@ template // T is char, int, etc data type using safe_t = boost::numeric::safe< T, pic16_promotion, - boost::numeric::throw_exception // use for compiling and running tests + exception_policy >; using safe_bool_t = boost::numeric::safe_unsigned_range< 0, 1, pic16_promotion, - boost::numeric::throw_exception // use for compiling and running tests + exception_policy >; using step_t = boost::numeric::safe_signed_range< 0, 1000, pic16_promotion, - boost::numeric::throw_exception + exception_policy >; using denom_t = boost::numeric::safe_signed_range< - -4001, - 4001, + -4005, + 4005, pic16_promotion, - boost::numeric::throw_exception + exception_policy >; using phase_ix_t = boost::numeric::safe_unsigned_range< 0, 3, pic16_promotion, - boost::numeric::throw_exception + exception_policy +>; +using c24_t = boost::numeric::safe_signed_range< + -0x8000000, + 0x7ffffff, + pic16_promotion, + exception_policy >; #define literal(x) boost::numeric::safe_literal{} @@ -76,13 +84,12 @@ using phase_ix_t = boost::numeric::safe_unsigned_range< void test(step_t m){ std::cout << "move motor to " << m << '\n'; - int i = 0; motor_run(m); - do{ + while(true == run_flg){ isr_motor_step(); - std::cout << ++i << ' ' << c32 << ' ' << c << '\n'; + std::cout << motor_pos << ' ' << c32 << ' ' << c << '\n'; std::this_thread::sleep_for(std::chrono::microseconds(ccpr)); - }while(true == run_flg); + } } int main() diff --git a/examples/motor3.c b/examples/motor3.c index 8316c48..f65f469 100644 --- a/examples/motor3.c +++ b/examples/motor3.c @@ -35,6 +35,13 @@ // standard conforming code // f) limited phase_ix to 0-3 +// ************* +// "unfixable" problems +// +// anyting with ++ or -- can't be proved to not overflow +// same with +=, -=, etc. +// step_down = move - step_no; might assign negative value to unsigned + #include "motor3.h" #if !defined(DESKTOP) @@ -75,7 +82,7 @@ step_t step_no; // progress of move step_t step_down; // start of down-ramp step_t move; // total steps to move step_t midpt; // midpoint of move -signed_int32 c32; // 24.8 fixed point delay count +c24_t c32; // 24.8 fixed point delay count denom_t denom; // 4.n+1 in ramp algo // Config data to make CCP1&2 generate quadrature sequence on PHASE pins @@ -97,13 +104,13 @@ void phase_bump() if(phase_ix == 3) phase_ix = literal(0); else - ++phase_ix; + ++phase_ix; // *** anything with ++ else // phase_inc < 0 if(phase_ix == 0) phase_ix = literal(3); else - --phase_ix; + --phase_ix; // *** anything with -- phase = ccpPhase[phase_ix]; CCP1CON = phase & literal(0xff); // set CCP action on next match CCP2CON = phase >> literal(8); @@ -122,7 +129,7 @@ void isr_motor_step() denom = ((step_no - move) * literal(4)) + literal(1); if (!(move & literal(1))) { // even move: repeat last delay before decel - denom += literal(4); + denom += literal(4); // *** anything with += break; } } @@ -134,13 +141,14 @@ void isr_motor_step() break; } denom += literal(4); - c32 -= (c32 * 2) / denom; // ramp algorithm + c32 -= (c32 * literal(2)) / denom; // ramp algorithm *** anything with -= // beware confict with foreground code if long div not reentrant - c = (c32 + 128) / 256; // round 24.8format->int16 + c = ((c32 + literal(128)) / literal(256)) % literal(0xffff); // round 24.8format->int16 + if (c <= C_MIN) { // go to constant speed ramp_sts = ramp_max; - step_down = move - step_no; + step_down = move - step_no; // *** could assign negative value to unsigned c = C_MIN; break; } @@ -161,8 +169,8 @@ void isr_motor_step() } // switch (ramp_sts) if (ramp_sts!=ramp_idle) { - motor_pos += pos_inc; - ++step_no; + motor_pos += pos_inc; // *** anything with += + ++step_no; // *** anything with ++ could overflow CCPR2H = CCPR1H = (ccpr >> 8); // timer value at next CCP match CCPR2L = CCPR1L = (ccpr & 0xff); @@ -186,9 +194,9 @@ void motor_run(step_t pos_new) phase_inc = literal(1); } else return; // already there - midpt = (move - literal(1))>> literal(1); + midpt = (move - literal(1))>> literal(1); // *** would fail for move == 0 c = C0; - c32 = c * 256; // keep c in 24.8 fixed-point format for ramp calcs + c32 = c << 8; // keep c in 24.8 fixed-point format for ramp calcs step_no = literal(0); // step counter denom = literal(1); // 4.n+1, n=0 ramp_sts = ramp_up; // start ramp state-machine diff --git a/include/checked_result.hpp b/include/checked_result.hpp index 6ba61d7..90917fa 100644 --- a/include/checked_result.hpp +++ b/include/checked_result.hpp @@ -113,19 +113,15 @@ struct checked_result { constexpr bool exception() const { return m_e != exception_type::no_exception; } - - template - constexpr void - dispatch(){ - EP(m_e, m_msg); - } - }; template -constexpr void +void dispatch(const checked_result & cr){ - dispatch(cr.m_e, cr.m_msg); + if(cr.no_exception()) + dispatch(exception_type::no_exception, ""); + else + dispatch(cr.m_e, cr.m_msg); } // C++ does not (yet) permit constexpr lambdas. So create some diff --git a/include/exception.hpp b/include/exception.hpp index 72eb0bf..002eb6a 100644 --- a/include/exception.hpp +++ b/include/exception.hpp @@ -30,8 +30,8 @@ enum class exception_type { }; template -constexpr void -dispatch(const exception_type e, char const * const msg){ +void +dispatch(const exception_type & e, char const * msg){ switch(e){ case exception_type::overflow_error: EP::overflow_error(msg); diff --git a/include/exception_policies.hpp b/include/exception_policies.hpp index 93a40cc..4b00f34 100644 --- a/include/exception_policies.hpp +++ b/include/exception_policies.hpp @@ -25,12 +25,12 @@ namespace numeric { // this would emulate the normal C/C++ behavior of permitting overflows // and the like. struct ignore_exception { - constexpr static void no_error(const char * message) {} - constexpr static void uninitialized_error(const char * message) {} - constexpr static void overflow_error(const char * message) {} - constexpr static void underflow_error(const char * message) {} - constexpr static void range_error(const char * message) {} - constexpr static void domain_error(const char * message) {} + static void no_error(const char * message) {} + static void uninitialized_error(const char * message) {} + static void overflow_error(const char * message) {} + static void underflow_error(const char * message) {} + static void range_error(const char * message) {} + static void domain_error(const char * message) {} }; // example - if you want to specify specific behavior for particular exception @@ -46,22 +46,22 @@ template< void (*DOMAIN)(const char *) = *OVERFLOW > struct no_exception_support { - constexpr static void no_error(const char * message) { + static void no_error(const char * message) { NO_EXCEPTION(message); } - constexpr static void uninitialized_error(const char * message) { + static void uninitialized_error(const char * message) { UNINITIALIZED(message); } - constexpr static void overflow_error(const char * message) { + static void overflow_error(const char * message) { OVERFLOW(message); } - constexpr static void underflow_error(const char * message) { + static void underflow_error(const char * message) { UNDERFLOW(message); } - constexpr static void range_error(const char * message) { + static void range_error(const char * message) { RANGE(message); } - constexpr static void domain_error(const char * message) { + static void domain_error(const char * message) { DOMAIN(message); } }; @@ -69,21 +69,21 @@ struct no_exception_support { // If an exceptional condition is detected at runtime throw the exception. // map our exception list to the ones in stdexcept struct throw_exception { - constexpr static void no_error(const char * message) { + static void no_error(const char * message) { } - constexpr static void unintialized_error(const char * message) { + static void unintialized_error(const char * message) { throw std::invalid_argument(message); } - constexpr static void overflow_error(const char * message) { + static void overflow_error(const char * message) { throw std::overflow_error(message); } - constexpr static void underflow_error(const char * message) { + static void underflow_error(const char * message) { throw std::underflow_error(message); } - constexpr static void range_error(const char * message) { + static void range_error(const char * message) { throw std::range_error(message); } - constexpr static void domain_error(const char * message) { + static void domain_error(const char * message) { throw std::domain_error(message); } }; diff --git a/include/interval.hpp b/include/interval.hpp index 742b050..89d51fb 100644 --- a/include/interval.hpp +++ b/include/interval.hpp @@ -66,22 +66,18 @@ struct interval { // other inteval t template constexpr bool includes(const interval & t) const { - // note very tricky algebra here. the <= and >= operators - // on checked_result yield tribool. If either argument is an exception - // condition, he result is indeterminate. The result of && on two - // tribools is indeterminant if either is indeterminate. return - safe_compare::less_than_equal(l, t.l) + safe_compare::greater_than_equal(t.l, l) && - safe_compare::greater_than_equal(u, t.u) + safe_compare::less_than_equal(t.u, u) ; } template constexpr bool includes(const T & t) const { return - ! safe_compare::less_than(u,t) + safe_compare::greater_than_equal(t, l) && - ! safe_compare::less_than(t,l) + safe_compare::less_than_equal(t, u) ; } }; diff --git a/include/safe_base_operations.hpp b/include/safe_base_operations.hpp index 5e3054e..ec12386 100644 --- a/include/safe_base_operations.hpp +++ b/include/safe_base_operations.hpp @@ -43,13 +43,13 @@ struct validate_detail { template constexpr static R return_value( const T & t, - const interval & this_interval + const interval & r_interval ){ // INT08-C - if(! this_interval.includes(t)) + if(! r_interval.includes(t)) E::range_error("Value out of range for this safe type"); checked_result r = checked::cast(t); - assert(r.no_exception()); + dispatch(r); return r; } }; @@ -79,6 +79,7 @@ validated_cast(const T & t) const { indeterminate(t_interval < this_interval), "safe type cannot be constructed with this type" ); + return boost::mpl::if_c< this_interval.includes(t_interval), typename validate_detail::exception_not_possible, @@ -276,7 +277,8 @@ struct addition_result { // when we add the temporary intervals above, we'll get a new interval // with the correct range for the sum ! - constexpr static const checked_result> r_interval = add(t_interval, u_interval); + constexpr static const checked_result> r_interval = + add(t_interval, u_interval); constexpr static bool exception_possible() { return ! r_interval.no_exception(); @@ -412,7 +414,7 @@ struct subtraction_result { base_value(t), base_value(u) ); - dispatch(r); + dispatch(r); return static_cast(r); } @@ -527,7 +529,7 @@ struct multiplication_result { base_value(t), base_value(u) ); - boost::numeric::dispatch(r); + dispatch(r); return static_cast(r); } @@ -1215,7 +1217,10 @@ constexpr inline operator|(const T & t, const U & u){ base_value(u) ); assert(r.no_exception()); - return static_cast(r); + return typename bwr::type( + static_cast(r), + std::false_type() // don't need to re-validate + ); } template @@ -1283,7 +1288,10 @@ constexpr inline operator&(const T & t, const U & u){ base_value(u) ); assert(r.no_exception()); - return static_cast(r); + return typename bwr::type( + static_cast(r), + std::false_type() // don't need to re-validate + ); } template @@ -1316,7 +1324,10 @@ constexpr inline operator^(const T & t, const U & u){ const checked_result r = checked::bitwise_xor(t, u); assert(r.no_exception()); - return static_cast(r); + return typename bwr::type( + static_cast(r), + std::false_type() // don't need to re-validate + ); } template diff --git a/include/safe_literal.hpp b/include/safe_literal.hpp index a06c3d2..c75270a 100644 --- a/include/safe_literal.hpp +++ b/include/safe_literal.hpp @@ -23,7 +23,7 @@ template class safe_literal_impl; template -struct is_safe > : public std::true_type +struct is_safe > : public std::false_type {}; template diff --git a/test/test_interval.cpp b/test/test_interval.cpp index c762d1b..15d196f 100644 --- a/test/test_interval.cpp +++ b/test/test_interval.cpp @@ -342,9 +342,11 @@ int main(){ test5() && test5() && test5() && + test5() && test5() && test5() && test5() && + test5() && test4::test1() && test4::test2() && test4::test3() && diff --git a/test/test_range.cpp b/test/test_range.cpp index 24d1f9a..9d1bba2 100644 --- a/test/test_range.cpp +++ b/test/test_range.cpp @@ -69,7 +69,7 @@ using safe_t = boost::numeric::safe_signed_range< bool test2(){ std::cout << "test1" << std::endl; try{ - constexpr const safe_t<-64, 63> x(1); + const safe_t<-64, 63> x(1); safe_t<-64, 63> y; y = 2; std::cout << "x = " << x << std::endl;