From 29cf7f3d1238db0730da069c602fdefda2d8a04b Mon Sep 17 00:00:00 2001 From: Robert Ramey Date: Mon, 11 Jan 2016 11:01:15 -0800 Subject: [PATCH] progress on example 93 to demonstrate trapping all potential exceptions at compile time removed constexpr from exception policies. this eliminates obstacle to gcc compilation which doesn't support constexpr throw unfortunately, all versions of gcc trip compiler fault so gcc not supported for now safe_literal - make this an unsafe type since it doesn't have policies - this might change in the future --- CMake/CMakeLists.txt | 4 ++-- examples/example84.cpp | 2 +- examples/example91.cpp | 5 +++-- examples/example93.cpp | 29 +++++++++++++++---------- examples/motor3.c | 30 ++++++++++++++++---------- include/checked_result.hpp | 14 +++++-------- include/exception.hpp | 4 ++-- include/exception_policies.hpp | 36 ++++++++++++++++---------------- include/interval.hpp | 12 ++++------- include/safe_base_operations.hpp | 29 +++++++++++++++++-------- include/safe_literal.hpp | 2 +- test/test_interval.cpp | 2 ++ test/test_range.cpp | 2 +- 13 files changed, 96 insertions(+), 75 deletions(-) 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;