diff --git a/CMake/CMakeLists.txt b/CMake/CMakeLists.txt index ee218c3..e7887a0 100644 --- a/CMake/CMakeLists.txt +++ b/CMake/CMakeLists.txt @@ -197,11 +197,15 @@ foreach(file_path ${example_list}) add_test(NAME ${base_name} COMMAND ${base_name}) endforeach(file_path) -set_target_properties(example81 example9 PROPERTIES +set_target_properties(test_z example84 example9 PROPERTIES EXCLUDE_FROM_ALL TRUE EXCLUDE_FROM_DEFAULT_BUILD TRUE ) +set_target_properties(example81 PROPERTIES + WILL_FAIL TRUE +) + # end examples targets #################### diff --git a/doc/boostbook/safe_introduction.xml b/doc/boostbook/safe_introduction.xml index 21f830c..8a783e5 100644 --- a/doc/boostbook/safe_introduction.xml +++ b/doc/boostbook/safe_introduction.xml @@ -42,7 +42,7 @@ int f(int x, int y){ if (((y > 0) && (x > (INT_MAX - y))) - || ((y < 0) && (x < (INT_MIN - x)))) { + || ((y < 0) && (x < (INT_MIN - y)))) { /* Handle error */ } return x + y; @@ -104,7 +104,7 @@ safe<int> f(safe<int> x, safe<int> y){ arithmetic expression will yield incorrect results. -
+
Implementation All facilities modern C++ are employed to minimize runtime overhead @@ -128,7 +128,7 @@ safe<int> f(safe<int> x, safe<int> y){ numeric expressions is misleading.
-
+
Additional Features Operation of safe types is determined by template parameters which @@ -186,7 +186,7 @@ safe<int> f(safe<int> x, safe<int> y){ Enforce of other program requirements using ranged integer types. The library includes types safe_..._range<Min, Max> and safe_literal(...). These types can be used to improve program - correctness and performance. + correctness and performance.
diff --git a/examples/Generate stepper-motor speed profiles in real time | Embedded.pdf b/examples/Generate stepper-motor speed profiles in real time | Embedded.pdf new file mode 100644 index 0000000..7088a77 Binary files /dev/null and b/examples/Generate stepper-motor speed profiles in real time | Embedded.pdf differ diff --git a/examples/Motor.c b/examples/Motor.c new file mode 100644 index 0000000..3c5f750 --- /dev/null +++ b/examples/Motor.c @@ -0,0 +1,182 @@ +// Demo program for stepper motor control with linear ramps +// Hardware: PIC18F252, L6219 +//#include "18F252.h" + +// PIC18F252 SFRs +/* +#byte TRISC = 0xf94 +#byte T3CON = 0xfb1 +#byte CCP2CON = 0xfba +#byte CCPR2L = 0xfbb +#byte CCPR2H = 0xfbc +#byte CCP1CON = 0xfbd +#byte CCPR1L = 0xfbe +#byte CCPR1H = 0xfbf +#byte T1CON = 0xfcd +#byte TMR1L = 0xfce +#byte TMR1H = 0xfcf +#bit TMR1ON = T1CON.0 +*/ + +// 1st step=50ms; max speed=120rpm (based on 1MHz timer, 1.8deg steps) +#define C0 50000 +#define C_MIN 2500 + +// ramp state-machine states +#define ramp_idle 0 +#define ramp_up 1 +#define ramp_max 2 +#define ramp_down 3 +#define ramp_last 4 + +// Types: int8,int16,int32=8,16,32bit integers, unsigned by default +int8 ramp_sts=ramp_idle; +signed int16 motor_pos = 0; // absolute step number +signed int16 pos_inc=0; // motor_pos increment +int16 phase=0; // ccpPhase[phase_ix] +int8 phase_ix=0; // index to ccpPhase[] +int8 phase_inc; // phase_ix increment +int8 run_flg; // true while motor is running +int16 ccpr; // copy of CCPR1&2 +int16 c; // integer delay count +int16 step_no; // progress of move +int16 step_down; // start of down-ramp +int16 move; // total steps to move +int16 midpt; // midpoint of move +int32 c32; // 24.8 fixed point delay count +signed int16 denom; // 4.n+1 in ramp algo + +// Config data to make CCP1&2 generate quadrature sequence on PHASE pins +// Action on CCP match: 8=set+irq; 9=clear+irq +int16 const ccpPhase[] = {0x909, 0x908, 0x808, 0x809}; // 00,01,11,10 + +void current_on(){/* code as needed */} // motor drive current +void current_off(){/* code as needed */} // reduce to holding value + +// compiler-specific ISR declaration +#INT_CCP1 +void isr_motor_step() +{ // CCP1 match -> step pulse + IRQ + ccpr += c; // next comparator value: add step delay count + switch (ramp_sts) + { + case ramp_up: // accel + if (step_no==midpt) + { // midpoint: decel + ramp_sts = ramp_down; + denom = ((step_no - move)<<2)+1; + if (!(move & 1)) + { // even move: repeat last delay before decel + denom +=4; + break; + } + } + // no break: share code for ramp algo + case ramp_down: // decel + if (step_no == move-1) + { // next irq is cleanup (no step) + ramp_sts = ramp_last; + break; + } + denom+=4; + c32 -= (c32<<1)/denom; // ramp algorithm + // beware confict with foreground code if long div not reentrant + c = (c32+128)>>8; // round 24.8format->int16 + if (c <= C_MIN) + { // go to constant speed + ramp_sts = ramp_max; + step_down = move - step_no; + c = C_MIN; + break; + } + break; + case ramp_max: // constant speed + if (step_no == step_down) + { // start decel + ramp_sts = ramp_down; + denom = ((step_no - move)<<2)+5; + } + break; + default: // last step: cleanup + ramp_sts = ramp_idle; + current_off(); // reduce motor current to holding value + disable_interrupts(INT_CCP1); + run_flg = FALSE; // move complete + break; + } // switch (ramp_sts) + if (ramp_sts!=ramp_idle) + { + motor_pos += pos_inc; + ++step_no; + CCPR2H = CCPR1H = (ccpr >> 8); // timer value at next CCP match + CCPR2L = CCPR1L = (ccpr & 0xff); + if (ramp_sts!=ramp_last) // else repeat last action: no step + phase_ix = (phase_ix + phase_inc) & 3; + phase = ccpPhase[phase_ix]; + CCP1CON = phase & 0xff; // set CCP action on next match + CCP2CON = phase >> 8; + } // if (ramp_sts != ramp_idle) +} // isr_motor_step() + + +void motor_run(short pos_new) +{ // set up to drive motor to pos_new (absolute step#) + if (pos_new < motor_pos) // get direction & #steps + { + move = motor_pos-pos_new; + pos_inc = -1; + phase_inc = 0xff; + } + else if (pos_new != motor_pos) + { + move = pos_new-motor_pos; + pos_inc = 1; + phase_inc = 1; + } + else return; // already there + midpt = (move-1)>>1; + c = C0; + c32 = c<<8; // keep c in 24.8 fixed-point format for ramp calcs + step_no = 0; // step counter + denom = 1; // 4.n+1, n=0 + ramp_sts = ramp_up; // start ramp state-machine + run_flg = TRUE; + TMR1ON = 0; // stop timer1; + ccpr = make16(TMR1H,TMR1L); // 16bit value of Timer1 + ccpr += 1000; // 1st step + irq 1ms after timer1 restart + CCPR2H = CCPR1H = (ccpr >> 8); + CCPR2L = CCPR1L = (ccpr & 0xff); + phase_ix = (phase_ix + phase_inc) & 3; + phase = ccpPhase[phase_ix]; + CCP1CON = phase & 0xff; // sets action on match + CCP2CON = phase >> 8; + current_on(); // current in motor windings + enable_interrupts(INT_CCP1); + TMR1ON=1; // restart timer1; +} // motor_run() + + +void initialize() +{ + disable_interrupts(GLOBAL); + disable_interrupts(INT_CCP1); + disable_interrupts(INT_CCP2); + output_c(0); + set_tris_c(0); + T3CON = 0; + T1CON = 0x35; + enable_interrupts(GLOBAL); +} // initialize() + +void main() +{ + initialize(); + while (1) + { // repeat 5 revs forward & back + motor_run(1000); + while (run_flg); + motor_run(0); + while (run_flg); + } +} // main() +// end of file motor.c diff --git a/examples/example9.cpp b/examples/example9.cpp index 0c56280..f24bd63 100644 --- a/examples/example9.cpp +++ b/examples/example9.cpp @@ -1,20 +1,23 @@ -#include -#include -#include +////////////////////////////////////////////////////////////////// +// test wrapper to permit compilation execution and debug of code +// intended for the PIC family of processors on the desktop +// development environment. +// +// Robert Ramey, 2015 + +#include #include -#include "../include/safe_integer.hpp" #include "../include/cpp.hpp" +#include "../include/automatic.hpp" +#include "../include/exception.hpp" +#include "../include/safe_integer.hpp" +#include "../include/safe_range.hpp" +#include "../include/safe_literal.hpp" -////////////////////////////////////////////////////////////// -// Stepper Motor Control - -// emululate evironment for pic162550 - -// data widths used by the CCS compiler for pic 16xxx series using pic16_promotion = boost::numeric::cpp< 8, // char - 8, // short - not used by pic 16xxxx + 8, // short 8, // int 16, // long 32 // long long @@ -23,412 +26,318 @@ using pic16_promotion = boost::numeric::cpp< template // T is char, int, etc data type using safe_t = boost::numeric::safe< T, + boost::numeric::automatic, + boost::numeric::trap_exception // use for compiling and running tests +>; +using safe_bool_t = boost::numeric::safe_unsigned_range< + 0, + 1, pic16_promotion, - boost::numeric::throw_exception // use for running tests + boost::numeric::trap_exception // use for compiling and running tests >; -using int8 = safe_t; -using int16 = safe_t; -using int32 = safe_t; +using int8 = safe_t; +using int16 = safe_t; +using int32 = safe_t; using uint8 = safe_t; using uint16 = safe_t; using uint32 = safe_t; +using signed_int16 = safe_t; -////////////////////////////////////////////////////////////// -// Mock defines, functions etc which are in he "real application -using BOOLEAN = bool; -#define TRUE true -#define FALSE false +#define literal(x) boost::numeric::safe_literal{} -using LEMPARAMETER = int16; +std::uint8_t base[0xfff]; +#define TRISC base[0xf94] +#define T3CON base[0xfb1] +#define CCP2CON base[0xfba] +#define CCPR2L base[0xfbb] +#define CCPR2H base[0xfbc] +#define CCP1CON base[0xfbd] +#define CCPR1L base[0xfbe] +#define CCPR1H base[0xfbf] +#define T1CON base[0xfcd] +#define TMR1L base[0xfce] +#define TMR1H base[0xfcf] +// implement equivalent to #bit in C++ -#define STEPS_PER_MM 200 -#define STEP 0 -#define STEP_LOW 0 -#define STEP_HIGH 1 -#define STEPPING_LIGHT 0 // Labeled D3 +// this types is meant to implement operations of naming bits +// which are part of a larger word. +// example +// unsigned int x. +// bit switch; // switch now refers to the +// second bit from the right of the variable x. So now can use: +// +// switch = 1; +// if(switch) +// ... -#define MAXIMUM_TIME 0xffff -#define INSTRUCTIONS_PER_SECOND ((uint32)MIPS * 0x100000) -// since we have a 48 mHz clock => -#define MIPS 12 - -// stepper motor limit switches -#define LIMIT_OUT 0 // input -#define LIMIT_IN 1 // input - -// switches are Normally/Closed -#define LIMIT_NOT_HIT 0 -#define LIMIT_HIT 1 - -// gecko microstepper output -#define DIRECTION_IN 0 -#define DIRECTION_OUT 1 -#define DIRECTION 0 -#define DIRECTION_LIGHT 0 -#define LIGHT_OFF 0 -#define LIGHT_ON 1 - -#define END_CLEARENCE 10 -#define HOME_OUT ((SLIDE_LENGTH - END_CLEARENCE) * STEPS_PER_MM) -#define HOME_IN (END_CLEARENCE * STEPS_PER_MM) -#define SLIDE_LENGTH 155 - -BOOLEAN report_arrival = FALSE; -typedef enum { - position_counter = 0, // in steps - // sets, initializes the position counter in steps - // response with new value when set and when stage stops - fault = 11, -} pcode_t; - -// fault codes -typedef enum { - response_queue_overflow = 0, - sample_queue_overflow = 1, - unanticipated_interrupt = 2, - oscillator_failure = 3, - low_voltage_detected = 4, - ad_max_rate_exceeded = 5, - unspecified_fault = 9 -} fcode_t; - -BOOLEAN input(uint8){ - return TRUE; -} -void output_bit(uint8, BOOLEAN){ -} -void delay_us(uint8){}; -// just factor out macro expansion to save memory -void enqueue_response( - pcode_t p, - LEMPARAMETER * v, - fcode_t f = unspecified_fault -){} - -#define MAIL_BOX(name, type) type name -#define mail_box_put(name, value) (name = value) -#define mail_box_get(name, destination) (destination = name) -#define mail_box_isempty(name) false - -MAIL_BOX(current_velocity, LEMPARAMETER); -MAIL_BOX(target_position, LEMPARAMETER); -MAIL_BOX(current_position, LEMPARAMETER); -MAIL_BOX(dt, uint16); - -struct { - // acceleration constant. application of a signal to the controller - // initiates movement according to the direction. The acceleration - // depends upon the voltage on the coil, current limiting and load on the - // stage. Generally this will be determined by experimentation. If its - // determined that the stepper is skipping steps, we should lower this constant - // to reflect the fact that things accelerate more slowly than we've assumed. - // if we want to move the stage faster we should increase the voltage, - // increase the maximum current, and DEcrease the value of this constant. - LEMPARAMETER acceleration; - // (5 * 200) steps/sec/sec // => 1 sec to reach nominal 5 mm/sec speed - - LEMPARAMETER max_velocity; // => 30 mm second => 5 sec for full travel ; - LEMPARAMETER min_velocity; // => 1 mm second => 150 sec for full travel; - // nominal value would be (5 * STEPS_PER_MM) => 150 mm travel in 30 sec - - // current state of stage - // velocity in steps / second - LEMPARAMETER current_velocity; // +/- mm/second depending on direction - LEMPARAMETER current_position; // current position in steps. - LEMPARAMETER target_position; - // 200 steps/mm * 152 mm travel gives maximum 30480 - LEMPARAMETER sampling_on; - // turn sampling on - turns light on also - LEMPARAMETER min_sample_position; - LEMPARAMETER max_sample_position; - // define the range of positions between samples will - // be gathered - LEMPARAMETER steps_per_sample; - // a power of two 1, 2, 4, 8, ... - // samples will be taken when the position counter modulo - // steps_per_sample is zero - - // the following are dependent on the above. They are updated whenever - // one of the variables they depend upon ar updated. - uint8 sample_mask; - // save some time by pre-calculating - // ~(lem.steps_per_sample - 1) which masks off the high - // order bits - uint8 sample_setup; - // magic constant for the a/d conversion at the proper rate -} lem = { - (5 * 1000), // acceleration - (30 * STEPS_PER_MM), // max_velocity - (1 * STEPS_PER_MM), // min_velocity - 0, // current_velocity - 0, // position_counter - 0, // target_position - 0, // sampling on - 800, // min_sample_position - 29680, // max_sample_position - 32, // steps_per_sample - 0x1f, //~(32 - 1), // sample mask}; - 0 +template +struct bit { + T & m_word; + bit(T & rhs) : + m_word(rhs) + {} + bit & operator=(const safe_bool_t & b){ + if(b) + m_word |= (1 << N); + else + m_word &= ~(1 << N); + return *this; + } + bit & operator=(const boost::numeric::safe_literal<0>){ + m_word &= ~(1 << N); + return *this; + } + bit & operator=(const boost::numeric::safe_literal<1>){ + m_word |= (1 << N); + return *this; + } + operator safe_bool_t () const { + return m_word >> N & 1; + } }; -// return value in steps +// now we can render +//#bit TMR1ON = T1CON.0 +// as +bit TMR1ON(T1CON); +// and use expressions such as TMR1ON = 0 + +// make a 16 bit value from two 8 bit ones +int16 make16(int8 h, int8 l){ + return (h << literal(8)) | l; +} + +#define disable_interrupts(x) +#define enable_interrupts(x) +#define output_c(x) +#define set_tris_c(x) +#define TRUE literal(1) +#define FALSE literal(0) + +// note changes to original source code +// signed int16 <- signed_int16 note '-' added +// commented out the #byte and #bit statements +// commented out the #INT_CCP1 +// void main() <- int main() +// added return 0 to main +// changed instances of x = 0 to x = literal(0) + +////////////////////////////////////////////////////////////////// +// motor.c +// david austin +// http://www.embedded.com/design/mcus-processors-and-socs/4006438/Generate-stepper-motor-speed-profiles-in-real-time +// DECEMBER 30, 2004 + +// Demo program for stepper motor control with linear ramps +// Hardware: PIC18F252, L6219 +// #include "18F252.h" + +// PIC18F252 SFRs /* -Use the formula: - stopping dist = v **2 / a / 2 +#byte TRISC = 0xf94 +#byte T3CON = 0xfb1 +#byte CCP2CON = 0xfba +#byte CCPR2L = 0xfbb +#byte CCPR2H = 0xfbc +#byte CCP1CON = 0xfbd +#byte CCPR1L = 0xfbe +#byte CCPR1H = 0xfbf +#byte T1CON = 0xfcd +#byte TMR1L = 0xfce +#byte TMR1H = 0xfcf +#bit TMR1ON = T1CON.0 */ -uint16 get_stopping_distance(LEMPARAMETER velocity){ - int32 d; - d = velocity * velocity; - d /= lem.acceleration; - d /= 2; - return d; -} -int8 get_acceleration( - LEMPARAMETER dp, - LEMPARAMETER velocity -){ - int8 a; - if(dp > 0){ - // target is farther out than we are - if(velocity > 0){ - // moving out - LEMPARAMETER sd; // stopping distance - sd = get_stopping_distance(velocity); - if(dp > sd){ - // far from the destination - if(velocity > lem.max_velocity) - a = -1; - else - if(velocity == lem.max_velocity) - a = 0; - else - a = 1; - } - else{ - // close to the destination - a = -1; - } - } - else - if(velocity < 0){ - // moving in - a = 1; // turn around - } - else - a = 1; +// 1st step=50ms; max speed=120rpm (based on 1MHz timer, 1.8deg steps) +#define C0 literal(50000) +#define C_MIN literal(2500) + +// ramp state-machine states +#define ramp_idle literal(0) +#define ramp_up literal(1) +#define ramp_max literal(2) +#define ramp_down literal(3) +#define ramp_last literal(4) + +// Types: int8,int16,int32=8,16,32bit integers, unsigned by default +int8 ramp_sts=ramp_idle; +signed_int16 motor_pos = literal(0); // absolute step number +signed_int16 pos_inc = literal(0); // motor_pos increment +int16 phase=literal(0); // ccpPhase[phase_ix] +int8 phase_ix=literal(0); // index to ccpPhase[] +int8 phase_inc; // phase_ix increment +int8 run_flg; // true while motor is running +int16 ccpr; // copy of CCPR1&2 +int16 c; // integer delay count +int16 step_no; // progress of move +int16 step_down; // start of down-ramp +int16 move; // total steps to move +int16 midpt; // midpoint of move +int32 c32; // 24.8 fixed point delay count +signed_int16 denom; // 4.n+1 in ramp algo + +// Config data to make CCP1&2 generate quadrature sequence on PHASE pins +// Action on CCP match: 8=set+irq; 9=clear+irq +int16 const ccpPhase[] = { + literal(0x909), + literal(0x908), + literal(0x808), + literal(0x809) +}; // 00,01,11,10 + +void current_on(){/* code as needed */} // motor drive current +void current_off(){/* code as needed */} // reduce to holding value + +// compiler-specific ISR declaration +// #INT_CCP1 +void isr_motor_step() +{ // CCP1 match -> step pulse + IRQ + ccpr += c; // next comparator value: add step delay count + switch (ramp_sts) + { + case ramp_up: // accel + if (step_no==midpt) + { // midpoint: decel + ramp_sts = ramp_down; + denom = ((step_no - move) << literal(2) )+literal(1); + + if (!(move & literal(1))) + { // even move: repeat last delay before decel + denom +=literal(4); + break; + } } - else - if(dp < 0){ - // we're farther out than the target - if(velocity < 0){ - // moving left - LEMPARAMETER sd; // stopping distance - sd = get_stopping_distance(velocity); - if(dp < -sd){ - // far from the destination - if(velocity < - lem.max_velocity) - a = 1; - else - if(velocity == - lem.max_velocity) - a = 0; - else - a = -1; - } - else{ - // close to the destination - a = 1; - } - } - else - if(velocity > 0){ - // moving right - a = -1; - } - else - a = -1; + // no break: share code for ramp algo + case ramp_down: // decel + if (step_no == move-literal(1)) + { // next irq is cleanup (no step) + ramp_sts = ramp_last; + break; } - else{ - // we're there - if(velocity < - lem.min_velocity) - a = 1; - else - if(velocity > lem.min_velocity) - a = -1; - else - a = 0; // shouild never get here + denom+=4; + c32 -= (c32 << literal(1)) / denom; // ramp algorithm + // beware confict with foreground code if long div not reentrant + c = (c32+literal(128))>>literal(8); // round 24.8format->int16 + if (c <= C_MIN) + { // go to constant speed + ramp_sts = ramp_max; + step_down = move - step_no; + c = C_MIN; + break; } - return a; -} - -// update velocity according to acceleration and -// distance to target postion -void motor_velocity_update(){ - int8 a; - static uint16 dt = MAXIMUM_TIME; - - static struct { - LEMPARAMETER current_position; - LEMPARAMETER target_position; - } shadow_lem; - LEMPARAMETER dp; // difference between target and current - LEMPARAMETER velocity; - LEMPARAMETER previous_position; - - if(mail_box_isempty(lem.current_position)) - return; - - mail_box_get(lem.current_position, shadow_lem.current_position); - mail_box_get(lem.target_position, shadow_lem.target_position); - - velocity = lem.current_velocity; - dp = shadow_lem.target_position - shadow_lem.current_position; - a = get_acceleration(dp, velocity); - - if(0 != a){ - uint32 dv; - int16 pcount; - pcount = shadow_lem.current_position - previous_position; - if(pcount < 0) - pcount = - pcount; - dv = (uint32)lem.acceleration * dt * pcount; - dv /= INSTRUCTIONS_PER_SECOND; - if(0 == dv) - return; - if(0 < a) - velocity += dv; - else - velocity -= dv; + break; + case ramp_max: // constant speed + if (step_no == step_down) + { // start decel + ramp_sts = ramp_down; + /* + denom = ((step_no - move)<> literal(8)); // timer value at next CCP match + CCPR2L = CCPR1L = (ccpr & literal(0xff)); + if (ramp_sts!=ramp_last) // else repeat last action: no step + phase_ix = (phase_ix + phase_inc) & literal(3); + phase = ccpPhase[phase_ix]; + CCP1CON = phase & literal(0xff); // set CCP action on next match + CCP2CON = phase >> literal(8); + } // if (ramp_sts != ramp_idle) +} // isr_motor_step() - // figure new pulse width - if(lem.min_velocity < velocity) - dt = INSTRUCTIONS_PER_SECOND / velocity; - else - if(- lem.min_velocity < velocity) - dt = MAXIMUM_TIME; - else - dt = - INSTRUCTIONS_PER_SECOND / velocity; - mail_box_put(current_velocity, velocity); - mail_box_put(dt, dt); +void motor_run(signed_int16 pos_new) +{ // set up to drive motor to pos_new (absolute step#) + if (pos_new < motor_pos) // get direction & #steps + { + move = motor_pos-pos_new; + pos_inc = literal(-1); + phase_inc = literal(0xff); + } + else if (pos_new != motor_pos) + { + move = pos_new-motor_pos; + pos_inc = literal(1); + phase_inc = literal(1); + } + else return; // already there + /* + midpt = (move-1)>>1; + */ + auto x1 = move - 1; + auto x2 = x1 >> 1; + midpt = x2; + c = C0; + c32 = c<> literal(8)); + CCPR2L = CCPR1L = (ccpr & literal(0xff)); + phase_ix = (phase_ix + phase_inc) & literal(3); + phase = ccpPhase[phase_ix]; + CCP1CON = phase & literal(0xff); // sets action on match + CCP2CON = phase >> literal(8); + current_on(); // current in motor windings + enable_interrupts(INT_CCP1); + TMR1ON=TRUE; // restart timer1; +} // motor_run() - // make sure input mail box is empty so that next time we get a fresh one - mail_box_get(current_position, shadow_lem.current_position); -} +void initialize() +{ + disable_interrupts(GLOBAL); + disable_interrupts(INT_CCP1); + disable_interrupts(INT_CCP2); + output_c(0); + set_tris_c(0); + T3CON = 0; + T1CON = 0x35; + enable_interrupts(GLOBAL); +} // initialize() -/////////////////////////////////////////////////////////////// -// invoked at main timer interrupt time +// test program +int main() +{ + std::cout << "start test\n"; + try{ + initialize(); + motor_run(literal(100)); -BOOLEAN check_collision(){ - static BOOLEAN collision_recovery = FALSE; - - if(collision_recovery){ - if(lem.target_position == lem.current_position) - collision_recovery = FALSE; - return TRUE; + // move motor to position 1000 + motor_run(literal(1000)); + while (run_flg){ + isr_motor_step(); } - if(LIMIT_HIT == input(LIMIT_IN)){ - if(LIMIT_HIT == input(LIMIT_IN)){ - lem.current_position = 0; - lem.target_position = HOME_IN; - lem.current_velocity = lem.min_velocity; - collision_recovery = TRUE; - mail_box_put(target_position, lem.target_position); - return TRUE; - } + // move back to position 0 + motor_run(literal(0)); + while (run_flg) + isr_motor_step(); } - else - if(LIMIT_HIT == input(LIMIT_OUT)){ - if(LIMIT_HIT == input(LIMIT_OUT)){ - lem.current_position = SLIDE_LENGTH * STEPS_PER_MM; - lem.target_position = HOME_OUT; - lem.current_velocity = - lem.min_velocity; - collision_recovery = TRUE; - mail_box_put(target_position, lem.target_position); - return TRUE; - } + catch(...){ + std::cout << "test interrupted\n"; + return 1; } - return FALSE; -} - -void motor_step(){ - output_bit(STEP, STEP_LOW); - delay_us(4); - output_bit(STEP, STEP_HIGH); -} - -void motor_increment(){ - output_bit(DIRECTION, DIRECTION_OUT); - #if(TARGET==DEVBOARD) - output_bit(STEPPING_LIGHT, LIGHT_ON); - output_bit(DIRECTION_LIGHT, DIRECTION_OUT); - #endif - ++lem.current_position; - motor_step(); -} - -void motor_decrement(){ - output_bit(DIRECTION, DIRECTION_IN); - #if(TARGET==DEVBOARD) - output_bit(STEPPING_LIGHT, LIGHT_ON); - output_bit(DIRECTION_LIGHT, DIRECTION_IN); - #endif - --lem.current_position; - motor_step(); -} - -BOOLEAN check_arrival(){ - if(lem.current_position != lem.target_position) - return FALSE; - #if(TARGET==DEVBOARD) - output_bit(STEPPING_LIGHT, LIGHT_OFF); - #endif - if(report_arrival){ - report_arrival = FALSE; - enqueue_response(position_counter, & lem.current_position); - } - return TRUE; -} - -void motor_update(){ - mail_box_get(current_velocity, lem.current_velocity); - check_collision(); - if(0 < lem.current_velocity){ - if(lem.min_velocity < lem.current_velocity){ - motor_increment(); - } - else{ - if(!check_arrival()) - motor_increment(); - } - } - else - if(0 > lem.current_velocity){ - if(-lem.min_velocity > lem.current_velocity){ - motor_decrement(); - } - else{ - if(!check_arrival()) - motor_decrement(); - } - } - else{ - check_arrival(); - } - mail_box_put(current_position, lem.current_position); -} - -int main(int argc, const char * argv[]){ - // problem: testing against other architectures - std::cout << "example 9: "; - std::cout << "testing against other architectures" - << std::endl; - - return 0; -} + std::cout << "end test\n"; + return literal(0); +} // main() +// end of file motor.c diff --git a/include/automatic.hpp b/include/automatic.hpp index 3d8ef2e..51326e0 100644 --- a/include/automatic.hpp +++ b/include/automatic.hpp @@ -161,7 +161,9 @@ struct automatic { /////////////////////////////////////////////////////////////////////// template struct subtraction_result { - using result_base_type = calculate_max_t; + // subtraction can result in negative result regardless of the + // operand types ! + using result_base_type = std::intmax_t; using t_base_type = typename base_type::type; using u_base_type = typename base_type::type; diff --git a/include/checked.hpp b/include/checked.hpp index 3624f46..e30ad05 100644 --- a/include/checked.hpp +++ b/include/checked.hpp @@ -105,8 +105,8 @@ namespace detail { // result unsigned template - typename boost::enable_if< - typename std::is_unsigned, + typename boost::enable_if_c< + std::is_unsigned::value, checked_result >::type constexpr add( @@ -127,8 +127,8 @@ namespace detail { // result signed template - typename boost::enable_if< - typename std::is_signed, + typename boost::enable_if_c< + std::is_signed::value, checked_result >::type constexpr add( @@ -538,39 +538,102 @@ constexpr modulus( namespace detail { -template -constexpr checked_result check_shift( +template +typename std::enable_if< + ! std::numeric_limits::is_signed, + checked_result +>::type +constexpr check_shift( const T & t, const U & u ) { - if(! std::numeric_limits::is_integer){ - return checked_result( - exception_type::domain_error, - "shift operation can only be applied to integers" - ); - } - if(! std::numeric_limits::is_integer){ - return checked_result( - exception_type::domain_error, - "number of bits to shift must be an integer" - ); - } - // INT34-CPP C++ standard paragraph 5.8 - if(u < 0){ - return checked_result( - exception_type::domain_error, - "shifting negative amount is undefined behavior" - ); - } - // INT34-CPP C++ standard paragraph 5.8 + // INT34-C C++ standard paragraph 5.8 if(u > std::numeric_limits::digits){ - return checked_result( + return checked_result( exception_type::domain_error, "shifting more bits than available is undefined behavior" ); } + return cast(t); +} - return u; +template +typename std::enable_if< + std::numeric_limits::is_signed, + checked_result +>::type +constexpr check_shift( + const T & t, + const U & u +) { + // INT34-C C++ standard paragraph 5.8 + if(std::numeric_limits::max() > std::numeric_limits::digits){ + if(u > std::numeric_limits::digits){ + return checked_result( + exception_type::domain_error, + "shifting more bits than available is undefined behavior" + ); + } + } + // INT34-C C++ standard paragraph 5.8 + if(u < 0){ + return checked_result( + exception_type::domain_error, + "shifting negative amount is undefined behavior" + ); + } + return cast(t); +} + +template +typename std::enable_if< + ! std::numeric_limits::is_signed, + checked_result +>::type +constexpr left_shift( + const R & r, + const U & u +){ + return static_cast(r << u); +} + +template +typename std::enable_if< + std::numeric_limits::is_signed, + checked_result +>::type +constexpr left_shift( + const R & r, + const U & u +){ + // cannot shift negative values to the left + return (r < 0) ? + checked_result( + exception_type::domain_error, + "shifting negative values off left is undefined" + ) + : + checked_result(r << u) + ; + + /* + // if a negative value is shifted left, then it could all of + // a sudden change sign - we inhibit this here. + if(r < 0){ + U ui = u; + R ri = r; + while(ui-- > 0){ + ri <<= 1; + if(ri >= 0){ + return checked_result( + exception_type::domain_error, + "shifting negative values off left is undefined" + ); + } + } + } + return r; + */ } } // detail @@ -581,30 +644,17 @@ constexpr checked_result left_shift( const T & t, const U & u ) { - const checked_result ux = detail::check_shift(t, u); - if(! ux.no_exception()) - return checked_result(ux.m_e, ux.m_msg); + // INT13-C Note: We don't enforce recommendation as acually written + // as it would break too many programs. Specifically, we permit signed + // integer operands but require that they not be negative. This we can only + // enforce at runtime. + + const checked_result rx = detail::check_shift(t, u); - //static_assert(u >= 0, "u cannot be negative"); - const checked_result rx = cast(t); if(! rx.no_exception()) return rx; - R r = static_cast(rx); - if(r < 0){ - U ui = u; - while(ui-- > 0){ - r <<= 1; - if(r >= 0){ - return checked_result( - exception_type::domain_error, - "shifting negative values off left is undefined" - ); - } - } - return r; - } - return checked_result(r << u); + return detail::left_shift(rx, u); } // right shift @@ -613,36 +663,63 @@ constexpr checked_result right_shift( const T & t, const U & u ) { - const checked_result ux = detail::check_shift(t, u); - if(! ux.no_exception()) - return checked_result(ux.m_e, ux.m_msg); + // INT13-C Note: We don't enforce recommendation as acually written + // as it would break too many programs. Specifically, we permit signed + // integer operand but require that it not be negative. This we can only + // enforce at runtime. + + const checked_result rx = detail::check_shift(t, u); - const checked_result rx = cast(t); if(! rx.no_exception()) return rx; - if(t < 0) + if(u > std::numeric_limits::digits){ return checked_result( exception_type::domain_error, - "right shift cannot be applied to negative number" + "shifting more bits than available is undefined behavior" ); - - return checked_result(static_cast(rx) >> ux); + } + return static_cast(rx) >> u; } /////////////////////////////////// // bitwise operations +namespace detail { + // INT13-C Note: We don't enforce recommendation as acually written + // as it would break too many programs. Specifically, we permit signed + // integer operand but require that it not be negative. This we can only + // enforce at runtime. + template + typename boost::enable_if< + typename std::is_signed, + bool + >::type + check_bitwise_operand(const T & t){ + return t >= 0; + } + + template + typename boost::disable_if< + typename std::is_signed, + bool + >::type + check_bitwise_operand(const T & t){ + return true; + } +} + template constexpr checked_result bitwise_or( const T & t, const U & u ) { - static_assert( - std::is_integral::value && std::is_signed::value - && std::is_integral::value && std::is_signed::value, - "only intrinsic unsigned integers permitted" - ); + if(! detail::check_bitwise_operand(t)) + return checked_result( + exception_type::domain_error, + "bitwise operands cannot be negative" + ); + const checked_result rt = cast(t); if(! rt.no_exception()) return rt; @@ -659,11 +736,11 @@ constexpr checked_result bitwise_and( const T & t, const U & u ) { - static_assert( - std::is_integral::value && std::is_signed::value - && std::is_integral::value && std::is_signed::value, - "only intrinsic unsigned integers permitted" - ); + if(! detail::check_bitwise_operand(t)) + return checked_result( + exception_type::domain_error, + "bitwise operands cannot be negative" + ); const checked_result rt = cast(t); if(! rt.no_exception()) return rt; @@ -680,11 +757,11 @@ constexpr checked_result bitwise_xor( const T & t, const U & u ) { - static_assert( - std::is_integral::value && std::is_signed::value - && std::is_integral::value && std::is_signed::value, - "only intrinsic unsigned integers permitted" - ); + if(! detail::check_bitwise_operand(t)) + return checked_result( + exception_type::domain_error, + "bitwise operands cannot be negative" + ); const checked_result rt = cast(t); if(! rt.no_exception()) return rt; diff --git a/include/checked_result.hpp b/include/checked_result.hpp index 7d4e9ce..a59995e 100644 --- a/include/checked_result.hpp +++ b/include/checked_result.hpp @@ -117,7 +117,7 @@ struct checked_result { template constexpr void -dispatch(const checked_result &cr){ +dispatch(const checked_result & cr){ dispatch(cr.m_e, cr.m_msg); } diff --git a/include/concept/promotion_policy.hpp b/include/concept/promotion_policy.hpp index ce3e766..c24a9d6 100644 --- a/include/concept/promotion_policy.hpp +++ b/include/concept/promotion_policy.hpp @@ -30,9 +30,12 @@ struct PromotionPolicy { typedef typename PP::template right_shift_result rs_type; typedef typename PP::template bitwise_result bw_type; - checked_result::type> test(){ + checked_result::type> divide(){ return PP::template divide::type>(0, 0); } + checked_result::type> modulus(){ + return PP::template modulus::type>(0, 0); + } }; diff --git a/include/cpp.hpp b/include/cpp.hpp index fa480d6..c7cedcf 100755 --- a/include/cpp.hpp +++ b/include/cpp.hpp @@ -26,16 +26,6 @@ #include "safe_common.hpp" #include "checked.hpp" -// forward declaration - safe type -template< - class Stored, - Stored Min, - Stored Max, - class P, // promotion polic - class E // exception policy -> -class safe_base; - namespace boost { namespace numeric { @@ -54,43 +44,113 @@ template< int LongLongBits > struct cpp { - template - using normalize = typename std::make_signed::type>::type; - using local_char_type = typename boost::int_t::exact; using local_short_type = typename boost::int_t::exact; using local_int_type = typename boost::int_t::exact; using local_long_type = typename boost::int_t::exact; using local_long_long_type = typename boost::int_t::exact; + /* + template + using normalize = typename std::make_signed< + typename std::remove_cv::type + >::type; + + // given a native type - return the equivalent local type + template + using local_type_0 = + typename boost::mpl::if_< + std::is_same, + local_char_type, + typename boost::mpl::if_< + std::is_same, + local_short_type, + typename boost::mpl::if_< + std::is_same, + local_int_type, + typename boost::mpl::if_< + std::is_same, + local_long_type, + typename boost::mpl::if_< + std::is_same, + local_long_long_type, + void + >::type>::type>::type>::type>::type; + + template + using local_type_1 = + typename boost::mpl::if_< + std::is_signed, + local_type_0, + typename std::make_unsigned< + local_type_0::type> + >::type + >::type; + + template + using local_type = + typename boost::mpl::if_< + std::is_const, + typename std::add_const< + local_type_1::type> + >::type, + typename boost::mpl::if_< + std::is_volatile, + typename std::add_volatile< + local_type_1::type> + >::type, + local_type_1 + >::type>::type; + // section 4.13 integer conversion rank template - using rank = - typename boost::mpl::if_< - std::is_same >, + using rank_helper = + typename boost::mpl::if_c< + std::is_same::value, std::integral_constant, typename boost::mpl::if_c< - sizeof(local_char_type) == sizeof(T), + std::is_same::value, std::integral_constant, typename boost::mpl::if_c< - sizeof(local_short_type) == sizeof(T), + std::is_same::value, std::integral_constant, typename boost::mpl::if_c< - sizeof(local_int_type) == sizeof(T), + std::is_same::value, std::integral_constant, typename boost::mpl::if_c< - sizeof(local_long_type) == sizeof(T), + std::is_same::value, std::integral_constant, typename boost::mpl::if_c< - sizeof(local_long_long_type) == sizeof(T), + std::is_same::value, std::integral_constant, void - >::type >::type >::type >::type >::type >::type; + >::type>::type>::type>::type>::type>::type; + */ + + template + using rank = + typename boost::mpl::if_c< + sizeof(char) == sizeof(T), + std::integral_constant, + typename boost::mpl::if_c< + sizeof(short) == sizeof(T), + std::integral_constant, + typename boost::mpl::if_c< + sizeof(int) == sizeof(T), + std::integral_constant, + typename boost::mpl::if_c< + sizeof(long) == sizeof(T), + std::integral_constant, + typename boost::mpl::if_c< + sizeof(long long) == sizeof(T), + std::integral_constant, + void + >::type >::type >::type >::type >::type; // section 4.5 integral promotions template using integral_promotion = typename boost::mpl::if_c< - rank::value < rank::value, + (rank::value < rank::value), local_int_type, T >::type; @@ -153,25 +213,25 @@ struct cpp { template using result_type = usual_arithmetic_conversions< - integral_promotion::type>, - integral_promotion::type> + integral_promotion, + integral_promotion >; template struct addition_result { - using type = typename result_type::type; + using type = result_type; }; template struct subtraction_result { - using type = typename result_type::type; + using type = result_type; }; template struct multiplication_result { - using type = typename result_type::type; + using type = result_type; }; template struct division_result { - using type = typename result_type::type; + using type = result_type; }; // forward to correct divide implementation template @@ -185,7 +245,7 @@ struct cpp { template struct modulus_result { - using type = typename result_type::type; + using type = result_type; }; // forward to correct modulus implementation template @@ -198,15 +258,15 @@ struct cpp { } template struct left_shift_result { - using type = typename result_type::type; + using type = result_type; }; template struct right_shift_result { - using type = typename result_type::type; + using type = result_type; }; template struct bitwise_result { - using type = typename result_type::type; + using type = result_type; }; }; diff --git a/include/exception.hpp b/include/exception.hpp index f838231..7598489 100644 --- a/include/exception.hpp +++ b/include/exception.hpp @@ -31,7 +31,7 @@ enum class exception_type { template constexpr void -dispatch(const exception_type & e, char const * const msg){ +dispatch(const exception_type e, char const * 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 60fdd0f..93a40cc 100644 --- a/include/exception_policies.hpp +++ b/include/exception_policies.hpp @@ -92,7 +92,6 @@ struct throw_exception { // would otherwise trap at runtime. Hence expressions such as i/j // will trap at compile time unless j can be guaranteed to not be zero. - // meant to be trap the case where a program MIGHT throw an exception struct trap_exception { }; diff --git a/include/interval.hpp b/include/interval.hpp index 70a739d..3625886 100644 --- a/include/interval.hpp +++ b/include/interval.hpp @@ -393,4 +393,5 @@ std::ostream & operator<<(std::ostream & os, const boost::numeric::interval struct validate_detail { - struct exception_possible { - template - constexpr static Stored return_value( - const T & t - ){ - // INT08-C - if(! interval().includes(t)) - E::range_error("Value out of range for this safe type"); - checked_result r = checked::cast(t); - if(!r.no_exception()){ - E::domain_error("Stored type cannot hold argument"); - } - return static_cast(r); + // exception possible + template + constexpr static Stored return_value( + const T & t, std::true_type + ){ + // INT08-C + if(! interval().includes(t)) + E::range_error("Value out of range for this safe type"); + checked_result r = checked::cast(t); + if(!r.no_exception()){ + E::domain_error("Stored type cannot hold argument"); } - - }; - struct exception_not_possible { - template - constexpr static Stored return_value( - const T & t - ){ - return static_cast(t); - } - }; + return static_cast(r); + } + // exception not possible + template + constexpr static Stored return_value( + const T & t, std::false_type + ){ + return static_cast(t); + } }; template @@ -79,13 +75,13 @@ validated_cast(const T & t) const { indeterminate(t_interval < this_interval), "safe type cannot be constructed with this type" ); - - using type = typename boost::mpl::if_c< - this_interval.includes(t_interval), - typename validate_detail::exception_not_possible, - typename validate_detail::exception_possible - >::type; - return type::template return_value(base_value(t)); + return validate_detail::template return_value( + base_value(t), + typename std::integral_constant< + bool, + ! this_interval.includes(t_interval) + >() + ); } template @@ -274,13 +270,13 @@ struct addition_result { using exception_policy = typename P::exception_policy; using promotion_policy = typename P::promotion_policy; - using result_base_type = - typename promotion_policy::template addition_result< - T, U - >::type; - using t_base_type = typename base_type::type; using u_base_type = typename base_type::type; + using result_base_type = + typename promotion_policy::template addition_result< + t_base_type, + u_base_type + >::type; // filter out case were overflow cannot occur // note: subtle trickery. Suppose t is safe_range. Then @@ -302,44 +298,46 @@ struct addition_result { constexpr static const checked_result> r_interval = add(t_interval, u_interval); - constexpr static const interval type_iterval = - r_interval.no_exception() ? - static_cast>(r_interval) - : + constexpr static bool exception_possible() { + return ! r_interval.no_exception(); + } + + constexpr static const interval type_interval = + exception_possible() ? interval{} + : + static_cast>(r_interval) ; using type = safe_base< result_base_type, - type_iterval.l, - type_iterval.u, + type_interval.l, + type_interval.u, promotion_policy, exception_policy >; - constexpr static bool no_exception() { - return r_interval.no_exception(); + // exception possible + constexpr static result_base_type + return_value(const T & t, const U & u, std::true_type){ + static_assert(exception_possible(), "implement runtime check"); + checked_result r = checked::add( + base_value(t), + base_value(u) + ); + dispatch(r); + return static_cast(r); } - struct exception_possible { - constexpr static result_base_type return_value(const T & t, const U & u){ - checked_result r = checked::add( - base_value(t), - base_value(u) - ); - dispatch(r); - return static_cast(r); - } - }; - - struct exception_not_possible { - constexpr static result_base_type return_value(const T & t, const U & u){ - return - static_cast(base_value(t)) - + static_cast(base_value(u)) - ; - } - }; + // exception not possible + constexpr static result_base_type + return_value(const T & t, const U & u, std::false_type){ + static_assert(! exception_possible(), "no runtime check"); + return + static_cast(base_value(t)) + + static_cast(base_value(u)) + ; + } }; template @@ -354,29 +352,30 @@ constexpr inline operator+(const T & t, const U & u){ // argument dependent lookup should guarentee that we only get here using ar = addition_result; return typename ar::type( - boost::mpl::if_c< - ar::no_exception(), - typename ar::exception_not_possible, - typename ar::exception_possible - >::type::return_value(t, u), + ar::return_value( + t, + u, + typename std::integral_constant() + ), std::false_type() // don't need to revalidate ); } ///////////////////////////////////////////////////////////////// // subtraction + template struct subtraction_result { using P = common_policies; using exception_policy = typename P::exception_policy; using promotion_policy = typename P::promotion_policy; - using result_base_type = - typename promotion_policy::template subtraction_result< - T, U - >::type; - using t_base_type = typename base_type::type; using u_base_type = typename base_type::type; + using result_base_type = + typename promotion_policy::template subtraction_result< + t_base_type, + u_base_type + >::type; constexpr static const interval t_interval{ base_value(std::numeric_limits::min()), @@ -389,48 +388,50 @@ struct subtraction_result { }; // when we add the temporary intervals above, we'll get a new interval - // with the correct range for the sum ! + // with the correct range for the difference ! constexpr static const checked_result> r_interval = subtract(t_interval, u_interval); - constexpr static const interval type_iterval = - r_interval.no_exception() ? - static_cast>(r_interval) - : + constexpr static bool exception_possible() { + return ! r_interval.no_exception(); + } + + constexpr static const interval type_interval = + exception_possible() ? interval{} + : + static_cast>(r_interval) ; using type = safe_base< result_base_type, - type_iterval.l, - type_iterval.u, + type_interval.l, + type_interval.u, promotion_policy, exception_policy >; - constexpr static bool no_exception() { - return r_interval.no_exception(); + // exception possible + constexpr static result_base_type + return_value(const T & t, const U & u, std::true_type){ + static_assert(exception_possible(), "implement runtime check"); + checked_result r = checked::subtract( + base_value(t), + base_value(u) + ); + dispatch(r); + return static_cast(r); } - struct exception_possible { - constexpr static result_base_type return_value(const T & t, const U & u){ - checked_result r = checked::subtract( - base_value(t), - base_value(u) - ); - dispatch(r); - return static_cast(r); - } - }; - - struct exception_not_possible { - constexpr static result_base_type return_value(const T & t, const U & u){ - return - static_cast(base_value(t)) - - static_cast(base_value(u)) - ; - } - }; + // exception not possible + constexpr static result_base_type + return_value(const T & t, const U & u, std::false_type){ + static_assert(! exception_possible(), "no runtime check"); + return + static_cast(base_value(t)) + - static_cast(base_value(u)) + ; + } }; template @@ -444,11 +445,11 @@ typename boost::lazy_enable_if< constexpr operator-(const T & t, const U & u){ using sr = subtraction_result; return typename sr::type( - boost::mpl::if_c< - sr::no_exception(), - typename sr::exception_not_possible, - typename sr::exception_possible - >::type::return_value(t, u), + sr::return_value( + t, + u, + typename std::integral_constant() + ), std::false_type() // don't need to revalidate ); } @@ -461,13 +462,13 @@ struct multiplication_result { using P = common_policies; using exception_policy = typename P::exception_policy; using promotion_policy = typename P::promotion_policy; - using result_base_type = - typename promotion_policy::template multiplication_result< - T, U - >::type; - using t_base_type = typename base_type::type; using u_base_type = typename base_type::type; + using result_base_type = + typename promotion_policy::template multiplication_result< + t_base_type, + u_base_type + >::type; // filter out case were overflow cannot occur // note: subtle trickery. Suppose t is safe_range. Then @@ -489,17 +490,21 @@ struct multiplication_result { constexpr static const checked_result> r_interval = multiply(t_interval, u_interval); - constexpr static const interval type_iterval = - r_interval.no_exception() ? - static_cast>(r_interval) - : + constexpr static bool exception_possible() { + return ! r_interval.no_exception(); + } + + constexpr static const interval type_interval = + exception_possible() ? interval{} + : + static_cast>(r_interval) ; using type = safe_base< result_base_type, - type_iterval.l, - type_iterval.u, + type_interval.l, + type_interval.u, promotion_policy, exception_policy >; @@ -508,25 +513,27 @@ struct multiplication_result { return r_interval.no_exception(); } - struct exception_possible { - constexpr static result_base_type return_value(const T & t, const U & u){ - checked_result r = checked::multiply( - base_value(t), - base_value(u) - ); - dispatch(r); - return static_cast(r); - } - }; + // exception possible + constexpr static result_base_type + return_value(const T & t, const U & u, std::true_type){ + static_assert(exception_possible(), "implement runtime check"); + checked_result r = checked::multiply( + base_value(t), + base_value(u) + ); + boost::numeric::dispatch(r); + return static_cast(r); + } - struct exception_not_possible { - constexpr static result_base_type return_value(const T & t, const U & u){ - return - static_cast(base_value(t)) - * static_cast(base_value(u)) - ; - } - }; + // exception not possible + constexpr static result_base_type + return_value(const T & t, const U & u, std::false_type){ + static_assert(! exception_possible(), "no runtime check"); + return + static_cast(base_value(t)) + * static_cast(base_value(u)) + ; + } }; template @@ -541,11 +548,11 @@ constexpr operator*(const T & t, const U & u){ // argument dependent lookup should guarentee that we only get here using mr = multiplication_result; return typename mr::type( - boost::mpl::if_c< - mr::no_exception(), - typename mr::exception_not_possible, - typename mr::exception_possible - >::type::return_value(t, u), + mr::return_value( + t, + u, + typename std::integral_constant() + ), std::false_type() // don't need to revalidate ); } @@ -558,13 +565,13 @@ struct division_result { using P = common_policies; using exception_policy = typename P::exception_policy; using promotion_policy = typename P::promotion_policy; - using result_base_type = - typename promotion_policy::template division_result< - T, U - >::type; - using t_base_type = typename base_type::type; using u_base_type = typename base_type::type; + using result_base_type = + typename promotion_policy::template division_result< + t_base_type, + u_base_type + >::type; constexpr static const interval t_interval{ base_value(std::numeric_limits::min()), @@ -579,50 +586,52 @@ struct division_result { constexpr static const checked_result> r_interval = divide_nz(t_interval, u_interval); - constexpr static const interval result_iterval = - r_interval.no_exception() ? - static_cast>(r_interval) - : - interval{} + constexpr static bool exception_possible() { + return + // if over/under flow or domain error possible + ! r_interval.no_exception() + // if the denominator can contain zero + || (u_interval.l <= 0 && u_interval.u >=0 ) ; + } + + constexpr static const interval type_interval = + exception_possible() ? + interval{} + : + static_cast>(r_interval) + ; + using type = safe_base< result_base_type, - result_iterval.l, - result_iterval.u, + type_interval.l, + type_interval.u, promotion_policy, exception_policy >; - constexpr static bool no_exception() { - return - // if no over/under flow or domain error possible - r_interval.no_exception() - // and if the denominator cannot contain zero - && (u_interval.l <= 0 && u_interval.u >=0 ) - ; + // exception possible + constexpr static result_base_type + return_value(const T & t, const U & u, std::true_type){ + static_assert(exception_possible(), "implement runtime check"); + checked_result r = checked::divide( + base_value(t), + base_value(u) + ); + dispatch(r); + return static_cast(r); } - struct exception_possible { - static type constexpr return_value(const T & t, const U & u){ - checked_result r = - promotion_policy::template divide( - base_value(t), - base_value(u) - ); - dispatch(r); - return static_cast(r); - } - }; - - struct exception_not_possible { - constexpr static type return_value(const T & t, const U & u){ - return - static_cast(base_value(t)) - / static_cast(base_value(u)) - ; - } - }; + // exception not possible + constexpr static result_base_type + return_value(const T & t, const U & u, std::false_type){ + static_assert(! exception_possible(), "no runtime check"); + return + static_cast(base_value(t)) + / static_cast(base_value(u)) + ; + } }; template @@ -637,11 +646,11 @@ constexpr operator/(const T & t, const U & u){ // argument dependent lookup should guarentee that we only get here using dr = division_result; return typename dr::type( - boost::mpl::if_c< - dr::no_exception(), - typename dr::exception_not_possible, - typename dr::exception_possible - >::type::return_value(t, u), + dr::return_value( + t, + u, + typename std::integral_constant() + ), std::false_type() // don't need to revalidate ); } @@ -654,13 +663,13 @@ struct modulus_result { typedef common_policies P; using exception_policy = typename P::exception_policy; using promotion_policy = typename P::promotion_policy; - using result_base_type = - typename promotion_policy::template modulus_result< - T, U - >::type; - using t_base_type = typename base_type::type; using u_base_type = typename base_type::type; + using result_base_type = + typename promotion_policy::template modulus_result< + t_base_type, + u_base_type + >::type; constexpr static const interval t_interval{ base_value(std::numeric_limits::min()), @@ -675,50 +684,52 @@ struct modulus_result { constexpr static const checked_result> r_interval = modulus_nz(t_interval, u_interval); - constexpr static const interval result_iterval = - r_interval.no_exception() ? - static_cast>(r_interval) - : + constexpr static bool exception_possible() { + return + // if over/under flow or domain error possible + ! r_interval.no_exception() + // if the denominator can contain zero + || (u_interval.l <= 0 && u_interval.u >=0 ) + ; + } + + constexpr static const interval type_interval = + exception_possible() ? interval{} + : + static_cast>(r_interval) ; using type = safe_base< result_base_type, - result_iterval.l, - result_iterval.u, + type_interval.l, + type_interval.u, promotion_policy, exception_policy >; - constexpr static bool no_exception() { - return - // if no over/under flow or domain error possible - ((u_interval.l > 0 || 0 > u_interval.u) - // and if the denominator cannot contain zero - && (u_interval.l <= 0 && u_interval.u >=0 )) - ; + + // exception possible + constexpr static result_base_type + return_value(const T & t, const U & u, std::true_type){ + static_assert(exception_possible(), "implement runtime check"); + checked_result r = checked::modulus( + base_value(t), + base_value(u) + ); + dispatch(r); + return static_cast(r); } - struct exception_possible { - constexpr static result_base_type return_value(const T & t, const U & u){ - checked_result r = checked::modulus( - base_value(t), - base_value(u) - ); - dispatch(r); - return static_cast(r); - } - }; - - struct exception_not_possible { - constexpr static result_base_type return_value(const T & t, const U & u){ - return - static_cast(base_value(t)) - % static_cast(base_value(u)) - ; - } - }; - + // exception not possible + constexpr static result_base_type + return_value(const T & t, const U & u, std::false_type){ + static_assert(! exception_possible(), "no runtime check"); + return + static_cast(base_value(t)) + % static_cast(base_value(u)) + ; + } }; template @@ -731,13 +742,12 @@ typename boost::lazy_enable_if< >::type inline operator%(const T & t, const U & u){ using mr = modulus_result; - return typename mr::type( - boost::mpl::if_c< - mr::no_exception(), - typename mr::exception_not_possible, - typename mr::exception_possible - >::type::return_value(t, u), + mr::return_value( + t, + u, + typename std::integral_constant() + ), std::false_type() // don't need to revalidate ); } @@ -896,13 +906,13 @@ struct left_shift_result { using P = common_policies; using exception_policy = typename P::exception_policy; using promotion_policy = typename P::promotion_policy; - using result_base_type = - typename promotion_policy::template left_shift_result< - T, U - >::type; - using t_base_type = typename base_type::type; using u_base_type = typename base_type::type; + using result_base_type = + typename promotion_policy::template left_shift_result< + t_base_type, + u_base_type + >::type; constexpr static const interval t_interval{ base_value(std::numeric_limits::min()), @@ -918,42 +928,46 @@ struct left_shift_result { left_shift(t_interval, u_interval) }; - constexpr static const interval result_iterval = - r_interval.no_exception() ? - static_cast>(r_interval) - : + constexpr static bool exception_possible() { + return ! r_interval.no_exception(); + } + + constexpr static const interval type_interval = + exception_possible() ? interval{} + : + static_cast>(r_interval) ; using type = safe_base< result_base_type, - result_iterval.l, - result_iterval.u, + type_interval.l, + type_interval.u, promotion_policy, exception_policy >; - constexpr static bool no_exception() { - return r_interval.no_exception(); + // exception possible + constexpr static result_base_type + return_value(const T & t, const U & u, std::true_type){ + static_assert(exception_possible(), "implement runtime check"); + checked_result r = checked::left_shift( + base_value(t), + base_value(u) + ); + dispatch(r); + return static_cast(r); } - struct exception_possible { - constexpr static type return_value(const T & t, const U & u){ - const checked_result r = checked::left_shift( - base_value(t), - base_value(u) - ); - dispatch(r); - return static_cast(r); - } - }; - - struct exception_not_possible { - constexpr static type return_value(const T & t, const U & u){ - // just return the normal calcuation - return t << u; - } - }; + // exception not possible + constexpr static result_base_type + return_value(const T & t, const U & u, std::false_type){ + static_assert(! exception_possible(), "no runtime check"); + return + static_cast(base_value(t)) + << static_cast(base_value(u)) + ; + } }; template @@ -970,13 +984,14 @@ typename boost::lazy_enable_if_c< constexpr inline operator<<(const T & t, const U & u){ // INT13-CPP using lsr = left_shift_result; - - return boost::mpl::if_c< - // if no over/under flow or domain error possible - lsr::no_exception(), - typename lsr::exception_not_possible, - typename lsr::exception_possible - >::type::return_value(t, u); + return typename lsr::type( + lsr::return_value( + t, + u, + typename std::integral_constant() + ), + std::false_type() // don't need to revalidate + ); } // right shift @@ -985,13 +1000,13 @@ struct right_shift_result { using P = common_policies; using exception_policy = typename P::exception_policy; using promotion_policy = typename P::promotion_policy; - using result_base_type = - typename promotion_policy::template right_shift_result< - T, U - >::type; - using t_base_type = typename base_type::type; using u_base_type = typename base_type::type; + using result_base_type = + typename promotion_policy::template right_shift_result< + t_base_type, + u_base_type + >::type; constexpr static const interval t_interval{ base_value(std::numeric_limits::min()), @@ -1007,42 +1022,47 @@ struct right_shift_result { right_shift(t_interval, u_interval) }; - constexpr static const interval result_iterval = - r_interval.no_exception() ? - static_cast>(r_interval) - : + constexpr static bool exception_possible() { + return ! r_interval.no_exception(); + } + + constexpr static const interval type_interval = + exception_possible() ? interval{} + : + static_cast>(r_interval) ; using type = safe_base< result_base_type, - result_iterval.l, - result_iterval.u, + type_interval.l, + type_interval.u, promotion_policy, exception_policy >; - constexpr static bool no_exception() { - return r_interval.no_exception(); + + // exception possible + constexpr static result_base_type + return_value(const T & t, const U & u, std::true_type){ + static_assert(exception_possible(), "implement runtime check"); + checked_result r = checked::right_shift( + base_value(t), + base_value(u) + ); + dispatch(r); + return static_cast(r); } - struct exception_possible { - constexpr static type return_value(const T & t, const U & u){ - const checked_result r = checked::right_shift( - base_value(t), - base_value(u) - ); - dispatch(r); - return static_cast(r); - } - }; - - struct exception_not_possible { - constexpr static type return_value(const T & t, const U & u){ - // just return the normal calcuation - return t >> u; - } - }; + // exception not possible + constexpr static result_base_type + return_value(const T & t, const U & u, std::false_type){ + static_assert(! exception_possible(), "no runtime check"); + return + static_cast(base_value(t)) + >> static_cast(base_value(u)) + ; + } }; template @@ -1059,13 +1079,14 @@ typename boost::lazy_enable_if_c< constexpr inline operator>>(const T & t, const U & u){ // INT13-CPP using rsr = right_shift_result; - - return boost::mpl::if_c< - // if no over/under flow or domain error possible - rsr::no_exception(), - typename rsr::exception_not_possible, - typename rsr::exception_possible - >::type::return_value(t, u); + return typename rsr::type( + rsr::return_value( + t, + u, + typename std::integral_constant() + ), + std::false_type() // don't need to revalidate + ); } ///////////////////////////////////////////////////////////////// @@ -1073,28 +1094,37 @@ constexpr inline operator>>(const T & t, const U & u){ // operator | template -struct bitwise_result { +struct bitwise_or_result { using P = common_policies; using exception_policy = typename P::exception_policy; using promotion_policy = typename P::promotion_policy; + using t_base_type = typename base_type::type; + using u_base_type = typename base_type::type; using result_base_type = - typename promotion_policy::template bitwise< - T, U + typename promotion_policy::template bitwise_result< + t_base_type, + u_base_type >::type; - static_assert( - std::numeric_limits::is_integer - && (! std::numeric_limits::is_signed) - && std::numeric_limits::is_integer - && (! std::numeric_limits::is_signed), - // INT13-C - "bitwise operations are only applicable to unsigned integers" - ); - static_assert( - std::numeric_limits::is_integer - && (! std::numeric_limits::is_signed), - // INT13-C - "bitwise operations should return unsigned integers" - ); + + using xtype = + typename boost::mpl::if_c< + sizeof(t_base_type) < sizeof(u_base_type), + U, + T + >::type; + + constexpr static const interval result_interval { + base_value(std::numeric_limits::min()), + base_value(std::numeric_limits::max()) + }; + + using type = safe_base< + result_base_type, + result_interval.l, + result_interval.u, + promotion_policy, + exception_policy + >; }; template @@ -1103,39 +1133,77 @@ typename boost::lazy_enable_if< boost::numeric::is_safe, boost::numeric::is_safe >, - bitwise_result + bitwise_or_result >::type constexpr inline operator|(const T & t, const U & u){ - using bwr = bitwise_result; + using bwr = bitwise_or_result; using result_base_type = typename bwr::result_base_type; - using result_type = typename bwr::result_type; using exception_policy = typename bwr::exception_policy; - checked_result r = - checked::bitwise_or(t, u); - dispatch(r); - return static_cast(static_cast(r)); + const checked_result r = + checked::bitwise_or( + base_value(t), + base_value(u) + ); + assert(r.no_exception()); + return static_cast(r); } // operator & +template +struct bitwise_and_result { + using P = common_policies; + using exception_policy = typename P::exception_policy; + using promotion_policy = typename P::promotion_policy; + using t_base_type = typename base_type::type; + using u_base_type = typename base_type::type; + using result_base_type = + typename promotion_policy::template bitwise_result< + t_base_type, + u_base_type + >::type; + + using xtype = + typename boost::mpl::if_c< + sizeof(t_base_type) < sizeof(u_base_type), + T, + U + >::type; + + constexpr static const interval result_interval { + base_value(std::numeric_limits::min()), + base_value(std::numeric_limits::max()) + }; + + using type = safe_base< + result_base_type, + result_interval.l, + result_interval.u, + promotion_policy, + exception_policy + >; +}; + template typename boost::lazy_enable_if< boost::mpl::or_< boost::numeric::is_safe, boost::numeric::is_safe >, - bitwise_result + bitwise_and_result >::type constexpr inline operator&(const T & t, const U & u){ - using bwr = bitwise_result; + using bwr = bitwise_and_result; using result_base_type = typename bwr::result_base_type; - using result_type = typename bwr::result_type; using exception_policy = typename bwr::exception_policy; - checked_result r = - checked::bitwise_and(t, u); - dispatch(r); - return static_cast(static_cast(r)); + const checked_result r = + checked::bitwise_and( + base_value(t), + base_value(u) + ); + assert(r.no_exception()); + return static_cast(r); } // operator ^ @@ -1145,18 +1213,17 @@ typename boost::lazy_enable_if< boost::numeric::is_safe, boost::numeric::is_safe >, - bitwise_result + bitwise_or_result >::type constexpr inline operator^(const T & t, const U & u){ - using bwr = bitwise_result; + using bwr = bitwise_or_result; using result_base_type = typename bwr::result_base_type; - using result_type = typename bwr::result_type; using exception_policy = typename bwr::exception_policy; - checked_result r = + const checked_result r = checked::bitwise_xor(t, u); - dispatch(r); - return static_cast(static_cast(r)); + assert(r.no_exception()); + return static_cast(r); } ///////////////////////////////////////////////////////////////// diff --git a/include/safe_literal.hpp b/include/safe_literal.hpp index 876e914..a06c3d2 100644 --- a/include/safe_literal.hpp +++ b/include/safe_literal.hpp @@ -14,8 +14,7 @@ #include // for intmax -#include "safe_range.hpp" -#include "native.hpp" +#include "utility.hpp" namespace boost { namespace numeric { @@ -81,10 +80,16 @@ public: }; template -using safe_literal = safe_literal_impl; +using safe_literal = safe_literal_impl< + typename boost::numeric::signed_stored_type, + N +>; template -using safe_unsigned_literal = safe_literal_impl; +using safe_unsigned_literal = safe_literal_impl< + typename boost::numeric::unsigned_stored_type, + N +>; } // numeric } // boost diff --git a/test/test_interval.cpp b/test/test_interval.cpp index a848199..a7c57c4 100644 --- a/test/test_interval.cpp +++ b/test/test_interval.cpp @@ -45,6 +45,24 @@ bool test3(){ return true; } +template +bool test5(){ + using namespace boost::numeric; + std::cout << "test5" << std::endl; + interval t; + std::cout << "t = " << t << std::endl; + interval u; + std::cout << "u = " << u << std::endl; + if(t.includes(u)) + std::cout << "t includes u "; + if(u.includes(t)) + std::cout << "u includes t"; + if(!t.includes(u) && ! u.includes(t)) + std::cout << "neither interval includes the other"; + std::cout << std::endl; + return ExpectedResult == t.includes(u); +} + #include namespace test4 { @@ -322,6 +340,9 @@ int main(){ test1() && test2() && test3() && + test5() && + test5() && + test5() && test4::test1() && test4::test2() && test4::test3() && diff --git a/test/test_z.cpp b/test/test_z.cpp index d827d39..93c013d 100644 --- a/test/test_z.cpp +++ b/test/test_z.cpp @@ -145,23 +145,100 @@ int main(){ */ return 0; } -#endif -#include -#include +#include "../include/cpp.hpp" +#include "../include/safe_common.hpp" -using namespace std; +using pic16_promotion = boost::numeric::cpp< + 8, // char + 8, // short + 8, // int + 16, // long + 32 // long long +>; + +typedef pic16_promotion::rank pr; + +int main(){ + return 0; +} + +#include "../include/cpp.hpp" +#include "../include/safe_common.hpp" + +using namespace boost::numeric; + +// create custom policy which emulates native one +using custom = cpp< + CHAR_BIT, + CHAR_BIT * sizeof(short), + CHAR_BIT * sizeof(int), + CHAR_BIT * sizeof(long), + CHAR_BIT * sizeof(long long) +>; + +template +using test = custom::rank; + +print_value pv_int; +typedef print_type> pt_r_int; + +print_value pv_long_long; +typedef print_type pt_long_long; +typedef print_type> p_r_long_long; + +print_value pv_local_long_long_type; +typedef print_type pt_local_long_long; +typedef print_type> p_r_local_long_long; + +typedef print_type< + std::is_same< + long long, + custom::local_long_long_type + >::type +> pt_same; + + +// testing trap +#include "../include/exception_policies.hpp" +#include "../include/safe_integer.hpp" + +using namespace boost::numeric; +template // T is char, int, etc data type +using safe_t = safe< + T, + native, + trap_exception // use for compiling and running tests +>; + +template +void test(){ + safe_t t; + safe_t u; + t + u; + t - u; + t * u; + t / u; + t % u; + t << u; + t >> u; + t | u; + t & u; + t ^ u; +} +int main(int argc, char *argv[]){ + test(); + test(); + test(); + test(); + // this is a compile only test - but since many build systems + // can't handle a compile-only test - make sure it passes trivially. + return 0; +} + +#endif int main(){ - int8_t x = 100; - int y = x * x; - cout << y << endl; - - uint32_t z1 = 100; - int8_t z2 = -100; - auto y2 = z1 * z2; - cout << y2 << endl; - return 0; }