summaryrefslogtreecommitdiffstats
path: root/src/boost/libs/safe_numerics/example/example7.cpp
blob: 076abfd8226f26ddc49e5fcf8d13b597e009ca65 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include <cassert>
#include <stdexcept>
#include <sstream>
#include <iostream>

#include <boost/safe_numerics/safe_integer_range.hpp>

// NOT using safe numerics - enforce program contract explicitly
// return total number of minutes
unsigned int contract_convert(
    const unsigned int & hours,
    const unsigned int & minutes
) {
    // check that parameters are within required limits
    // invokes a runtime cost EVERYTIME the function is called
    // and the overhead of supporting an interrupt.
    // note high runtime cost!
    if(minutes > 59)
        throw std::domain_error("minutes exceeded 59");
    if(hours > 23)
        throw std::domain_error("hours exceeded 23");
    return hours * 60 + minutes;
}

// Use safe numerics to enforce program contract automatically
// define convenient typenames for hours and minutes hh:mm
using hours_t = boost::safe_numerics::safe_unsigned_range<0, 23>;
using minutes_t = boost::safe_numerics::safe_unsigned_range<0, 59>;
using minutes_total_t = boost::safe_numerics::safe_unsigned_range<0, 59>;

// return total number of minutes
// type returned is safe_unsigned_range<0, 24*60 - 1>
auto convert(const hours_t & hours, const minutes_t & minutes) {
    // no need to test pre-conditions
    // input parameters are guaranteed to hold legitimate values
    // no need to test post-conditions
    // return value guaranteed to hold result
    return hours * 60 + minutes;
}

unsigned int test1(unsigned int hours, unsigned int minutes){
    // problem: checking of externally produced value can be expensive
    // invalid parameters - detected - but at a heavy cost
    return contract_convert(hours, minutes);
}

auto test2(unsigned int hours, unsigned int minutes){
    // solution: use safe numerics
    // safe types can be implicitly constructed base types
    // construction guarentees corectness
    // return value is known to fit in unsigned int
    return convert(hours, minutes);
}

auto test3(unsigned int hours, unsigned int minutes){
    // actually we don't even need the convert function any more
    return hours_t(hours) * 60 + minutes_t(minutes);
}

int main(int, const char *[]){
    std::cout << "example 7: ";
    std::cout << "enforce contracts with zero runtime cost" << std::endl;

    unsigned int total_minutes;

    try {
        total_minutes = test3(17, 83);
        std::cout << "total minutes = " << total_minutes << std::endl;
    }
    catch(const std::exception & e){
        std::cout << "parameter error detected" << std::endl;
    }

    try {
        total_minutes = test3(17, 10);
        std::cout << "total minutes = " << total_minutes << std::endl;
    }
    catch(const std::exception & e){
        // should never arrive here
        std::cout << "parameter error erroneously detected" << std::endl;
        return 1;
    }
    return 0;
}