1 #include <cassert>
2 #include <stdexcept>
3 #include <sstream>
4 #include <iostream>
5
6 #include <boost/safe_numerics/safe_integer_range.hpp>
7
8 // NOT using safe numerics - enforce program contract explicitly
9 // return total number of minutes
contract_convert(const unsigned int & hours,const unsigned int & minutes)10 unsigned int contract_convert(
11 const unsigned int & hours,
12 const unsigned int & minutes
13 ) {
14 // check that parameters are within required limits
15 // invokes a runtime cost EVERYTIME the function is called
16 // and the overhead of supporting an interrupt.
17 // note high runtime cost!
18 if(minutes > 59)
19 throw std::domain_error("minutes exceeded 59");
20 if(hours > 23)
21 throw std::domain_error("hours exceeded 23");
22 return hours * 60 + minutes;
23 }
24
25 // Use safe numerics to enforce program contract automatically
26 // define convenient typenames for hours and minutes hh:mm
27 using hours_t = boost::safe_numerics::safe_unsigned_range<0, 23>;
28 using minutes_t = boost::safe_numerics::safe_unsigned_range<0, 59>;
29 using minutes_total_t = boost::safe_numerics::safe_unsigned_range<0, 59>;
30
31 // return total number of minutes
32 // type returned is safe_unsigned_range<0, 24*60 - 1>
convert(const hours_t & hours,const minutes_t & minutes)33 auto convert(const hours_t & hours, const minutes_t & minutes) {
34 // no need to test pre-conditions
35 // input parameters are guaranteed to hold legitimate values
36 // no need to test post-conditions
37 // return value guaranteed to hold result
38 return hours * 60 + minutes;
39 }
40
test1(unsigned int hours,unsigned int minutes)41 unsigned int test1(unsigned int hours, unsigned int minutes){
42 // problem: checking of externally produced value can be expensive
43 // invalid parameters - detected - but at a heavy cost
44 return contract_convert(hours, minutes);
45 }
46
test2(unsigned int hours,unsigned int minutes)47 auto test2(unsigned int hours, unsigned int minutes){
48 // solution: use safe numerics
49 // safe types can be implicitly constructed base types
50 // construction guarentees corectness
51 // return value is known to fit in unsigned int
52 return convert(hours, minutes);
53 }
54
test3(unsigned int hours,unsigned int minutes)55 auto test3(unsigned int hours, unsigned int minutes){
56 // actually we don't even need the convert function any more
57 return hours_t(hours) * 60 + minutes_t(minutes);
58 }
59
main(int,const char * [])60 int main(int, const char *[]){
61 std::cout << "example 7: ";
62 std::cout << "enforce contracts with zero runtime cost" << std::endl;
63
64 unsigned int total_minutes;
65
66 try {
67 total_minutes = test3(17, 83);
68 std::cout << "total minutes = " << total_minutes << std::endl;
69 }
70 catch(const std::exception & e){
71 std::cout << "parameter error detected" << std::endl;
72 }
73
74 try {
75 total_minutes = test3(17, 10);
76 std::cout << "total minutes = " << total_minutes << std::endl;
77 }
78 catch(const std::exception & e){
79 // should never arrive here
80 std::cout << "parameter error erroneously detected" << std::endl;
81 return 1;
82 }
83 return 0;
84 }
85