| // saturating.cpp ----------------------------------------------------------// |
| |
| // Copyright 2008 Howard Hinnant |
| // Copyright 2008 Beman Dawes |
| // Copyright 2009 Vicente J. Botet Escriba |
| |
| // Distributed under the Boost Software License, Version 1.0. |
| // See http://www.boost.org/LICENSE_1_0.txt |
| |
| /* |
| This code was extracted by Vicente J. Botet Escriba from Beman Dawes time2_demo.cpp which |
| was derived by Beman Dawes from Howard Hinnant's time2_demo prototype. |
| Many thanks to Howard for making his code available under the Boost license. |
| The original code was modified to conform to Boost conventions and to section |
| 20.9 Time utilities [time] of the C++ committee's working paper N2798. |
| See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2798.pdf. |
| |
| time2_demo contained this comment: |
| |
| Much thanks to Andrei Alexandrescu, |
| Walter Brown, |
| Peter Dimov, |
| Jeff Garland, |
| Terry Golubiewski, |
| Daniel Krugler, |
| Anthony Williams. |
| */ |
| |
| #define _CRT_SECURE_NO_WARNINGS // disable VC++ foolishness |
| |
| #include <boost/chrono/chrono.hpp> |
| #include <boost/type_traits.hpp> |
| |
| #include <iostream> |
| |
| ////////////////////////////////////////////////////////// |
| //////////////////// User2 Example /////////////////////// |
| ////////////////////////////////////////////////////////// |
| |
| // Demonstrate User2: |
| // A "saturating" signed integral type is developed. This type has +/- infinity and a nan |
| // (like IEEE floating point) but otherwise obeys signed integral arithmetic. |
| // This class is subsequently used as the rep in boost::chrono::duration to demonstrate a |
| // duration class that does not silently ignore overflow. |
| #include <ostream> |
| #include <stdexcept> |
| #include <climits> |
| |
| namespace User2 |
| { |
| |
| template <class I> |
| class saturate |
| { |
| public: |
| typedef I int_type; |
| |
| static const int_type nan = int_type(int_type(1) << (sizeof(int_type) * CHAR_BIT - 1)); |
| static const int_type neg_inf = nan + 1; |
| static const int_type pos_inf = -neg_inf; |
| private: |
| int_type i_; |
| |
| // static_assert(std::is_integral<int_type>::value && std::is_signed<int_type>::value, |
| // "saturate only accepts signed integral types"); |
| // static_assert(nan == -nan && neg_inf < pos_inf, |
| // "saturate assumes two's complement hardware for signed integrals"); |
| |
| public: |
| saturate() : i_(nan) {} |
| explicit saturate(int_type i) : i_(i) {} |
| // explicit |
| operator int_type() const; |
| |
| saturate& operator+=(saturate x); |
| saturate& operator-=(saturate x) {return *this += -x;} |
| saturate& operator*=(saturate x); |
| saturate& operator/=(saturate x); |
| saturate& operator%=(saturate x); |
| |
| saturate operator- () const {return saturate(-i_);} |
| saturate& operator++() {*this += saturate(int_type(1)); return *this;} |
| saturate operator++(int) {saturate tmp(*this); ++(*this); return tmp;} |
| saturate& operator--() {*this -= saturate(int_type(1)); return *this;} |
| saturate operator--(int) {saturate tmp(*this); --(*this); return tmp;} |
| |
| friend saturate operator+(saturate x, saturate y) {return x += y;} |
| friend saturate operator-(saturate x, saturate y) {return x -= y;} |
| friend saturate operator*(saturate x, saturate y) {return x *= y;} |
| friend saturate operator/(saturate x, saturate y) {return x /= y;} |
| friend saturate operator%(saturate x, saturate y) {return x %= y;} |
| |
| friend bool operator==(saturate x, saturate y) |
| { |
| if (x.i_ == nan || y.i_ == nan) |
| return false; |
| return x.i_ == y.i_; |
| } |
| |
| friend bool operator!=(saturate x, saturate y) {return !(x == y);} |
| |
| friend bool operator<(saturate x, saturate y) |
| { |
| if (x.i_ == nan || y.i_ == nan) |
| return false; |
| return x.i_ < y.i_; |
| } |
| |
| friend bool operator<=(saturate x, saturate y) |
| { |
| if (x.i_ == nan || y.i_ == nan) |
| return false; |
| return x.i_ <= y.i_; |
| } |
| |
| friend bool operator>(saturate x, saturate y) |
| { |
| if (x.i_ == nan || y.i_ == nan) |
| return false; |
| return x.i_ > y.i_; |
| } |
| |
| friend bool operator>=(saturate x, saturate y) |
| { |
| if (x.i_ == nan || y.i_ == nan) |
| return false; |
| return x.i_ >= y.i_; |
| } |
| |
| friend std::ostream& operator<<(std::ostream& os, saturate s) |
| { |
| switch (s.i_) |
| { |
| case pos_inf: |
| return os << "inf"; |
| case nan: |
| return os << "nan"; |
| case neg_inf: |
| return os << "-inf"; |
| }; |
| return os << s.i_; |
| } |
| }; |
| |
| template <class I> |
| saturate<I>::operator I() const |
| { |
| switch (i_) |
| { |
| case nan: |
| case neg_inf: |
| case pos_inf: |
| throw std::out_of_range("saturate special value can not convert to int_type"); |
| } |
| return i_; |
| } |
| |
| template <class I> |
| saturate<I>& |
| saturate<I>::operator+=(saturate x) |
| { |
| switch (i_) |
| { |
| case pos_inf: |
| switch (x.i_) |
| { |
| case neg_inf: |
| case nan: |
| i_ = nan; |
| } |
| return *this; |
| case nan: |
| return *this; |
| case neg_inf: |
| switch (x.i_) |
| { |
| case pos_inf: |
| case nan: |
| i_ = nan; |
| } |
| return *this; |
| } |
| switch (x.i_) |
| { |
| case pos_inf: |
| case neg_inf: |
| case nan: |
| i_ = x.i_; |
| return *this; |
| } |
| if (x.i_ >= 0) |
| { |
| if (i_ < pos_inf - x.i_) |
| i_ += x.i_; |
| else |
| i_ = pos_inf; |
| return *this; |
| } |
| if (i_ > neg_inf - x.i_) |
| i_ += x.i_; |
| else |
| i_ = neg_inf; |
| return *this; |
| } |
| |
| template <class I> |
| saturate<I>& |
| saturate<I>::operator*=(saturate x) |
| { |
| switch (i_) |
| { |
| case 0: |
| switch (x.i_) |
| { |
| case pos_inf: |
| case neg_inf: |
| case nan: |
| i_ = nan; |
| } |
| return *this; |
| case pos_inf: |
| switch (x.i_) |
| { |
| case nan: |
| case 0: |
| i_ = nan; |
| return *this; |
| } |
| if (x.i_ < 0) |
| i_ = neg_inf; |
| return *this; |
| case nan: |
| return *this; |
| case neg_inf: |
| switch (x.i_) |
| { |
| case nan: |
| case 0: |
| i_ = nan; |
| return *this; |
| } |
| if (x.i_ < 0) |
| i_ = pos_inf; |
| return *this; |
| } |
| switch (x.i_) |
| { |
| case 0: |
| i_ = 0; |
| return *this; |
| case nan: |
| i_ = nan; |
| return *this; |
| case pos_inf: |
| if (i_ < 0) |
| i_ = neg_inf; |
| else |
| i_ = pos_inf; |
| return *this; |
| case neg_inf: |
| if (i_ < 0) |
| i_ = pos_inf; |
| else |
| i_ = neg_inf; |
| return *this; |
| } |
| int s = (i_ < 0 ? -1 : 1) * (x.i_ < 0 ? -1 : 1); |
| i_ = i_ < 0 ? -i_ : i_; |
| int_type x_i_ = x.i_ < 0 ? -x.i_ : x.i_; |
| if (i_ <= pos_inf / x_i_) |
| i_ *= x_i_; |
| else |
| i_ = pos_inf; |
| i_ *= s; |
| return *this; |
| } |
| |
| template <class I> |
| saturate<I>& |
| saturate<I>::operator/=(saturate x) |
| { |
| switch (x.i_) |
| { |
| case pos_inf: |
| case neg_inf: |
| switch (i_) |
| { |
| case pos_inf: |
| case neg_inf: |
| case nan: |
| i_ = nan; |
| break; |
| default: |
| i_ = 0; |
| break; |
| } |
| return *this; |
| case nan: |
| i_ = nan; |
| return *this; |
| case 0: |
| switch (i_) |
| { |
| case pos_inf: |
| case neg_inf: |
| case nan: |
| return *this; |
| case 0: |
| i_ = nan; |
| return *this; |
| } |
| if (i_ > 0) |
| i_ = pos_inf; |
| else |
| i_ = neg_inf; |
| return *this; |
| } |
| switch (i_) |
| { |
| case 0: |
| case nan: |
| return *this; |
| case pos_inf: |
| case neg_inf: |
| if (x.i_ < 0) |
| i_ = -i_; |
| return *this; |
| } |
| i_ /= x.i_; |
| return *this; |
| } |
| |
| template <class I> |
| saturate<I>& |
| saturate<I>::operator%=(saturate x) |
| { |
| // *this -= *this / x * x; // definition |
| switch (x.i_) |
| { |
| case nan: |
| case neg_inf: |
| case 0: |
| case pos_inf: |
| i_ = nan; |
| return *this; |
| } |
| switch (i_) |
| { |
| case neg_inf: |
| case pos_inf: |
| i_ = nan; |
| case nan: |
| return *this; |
| } |
| i_ %= x.i_; |
| return *this; |
| } |
| |
| // Demo overflow-safe integral durations ranging from picoseconds resolution to millennium resolution |
| typedef boost::chrono::duration<saturate<long long>, boost::pico > picoseconds; |
| typedef boost::chrono::duration<saturate<long long>, boost::nano > nanoseconds; |
| typedef boost::chrono::duration<saturate<long long>, boost::micro > microseconds; |
| typedef boost::chrono::duration<saturate<long long>, boost::milli > milliseconds; |
| typedef boost::chrono::duration<saturate<long long> > seconds; |
| typedef boost::chrono::duration<saturate<long long>, boost::ratio< 60LL> > minutes; |
| typedef boost::chrono::duration<saturate<long long>, boost::ratio< 3600LL> > hours; |
| typedef boost::chrono::duration<saturate<long long>, boost::ratio< 86400LL> > days; |
| typedef boost::chrono::duration<saturate<long long>, boost::ratio< 31556952LL> > years; |
| typedef boost::chrono::duration<saturate<long long>, boost::ratio<31556952000LL> > millennium; |
| |
| } // User2 |
| |
| // Demonstrate custom promotion rules (needed only if there are no implicit conversions) |
| namespace User2 { namespace detail { |
| |
| template <class T1, class T2, bool = boost::is_integral<T1>::value> |
| struct promote_helper; |
| |
| template <class T1, class T2> |
| struct promote_helper<T1, saturate<T2>, true> // integral |
| { |
| typedef typename boost::common_type<T1, T2>::type rep; |
| typedef User2::saturate<rep> type; |
| }; |
| |
| template <class T1, class T2> |
| struct promote_helper<T1, saturate<T2>, false> // floating |
| { |
| typedef T1 type; |
| }; |
| |
| } } |
| |
| namespace boost |
| { |
| |
| template <class T1, class T2> |
| struct common_type<User2::saturate<T1>, User2::saturate<T2> > |
| { |
| typedef typename common_type<T1, T2>::type rep; |
| typedef User2::saturate<rep> type; |
| }; |
| |
| template <class T1, class T2> |
| struct common_type<T1, User2::saturate<T2> > |
| : User2::detail::promote_helper<T1, User2::saturate<T2> > {}; |
| |
| template <class T1, class T2> |
| struct common_type<User2::saturate<T1>, T2> |
| : User2::detail::promote_helper<T2, User2::saturate<T1> > {}; |
| |
| |
| // Demonstrate specialization of duration_values: |
| |
| namespace chrono { |
| |
| template <class I> |
| struct duration_values<User2::saturate<I> > |
| { |
| typedef User2::saturate<I> Rep; |
| public: |
| static Rep zero() {return Rep(0);} |
| static Rep max BOOST_PREVENT_MACRO_SUBSTITUTION () {return Rep(Rep::pos_inf-1);} |
| static Rep min BOOST_PREVENT_MACRO_SUBSTITUTION () {return -(max)();} |
| }; |
| |
| } // namespace chrono |
| |
| } // namespace boost |
| |
| #include <iostream> |
| |
| void testUser2() |
| { |
| std::cout << "*************\n"; |
| std::cout << "* testUser2 *\n"; |
| std::cout << "*************\n"; |
| using namespace User2; |
| typedef seconds::rep sat; |
| years yr(sat(100)); |
| std::cout << "100 years expressed as years = " << yr.count() << '\n'; |
| nanoseconds ns = yr; |
| std::cout << "100 years expressed as nanoseconds = " << ns.count() << '\n'; |
| ns += yr; |
| std::cout << "200 years expressed as nanoseconds = " << ns.count() << '\n'; |
| ns += yr; |
| std::cout << "300 years expressed as nanoseconds = " << ns.count() << '\n'; |
| // yr = ns; // does not compile |
| std::cout << "yr = ns; // does not compile\n"; |
| // picoseconds ps1 = yr; // does not compile, compile-time overflow in ratio arithmetic |
| std::cout << "ps = yr; // does not compile\n"; |
| ns = yr; |
| picoseconds ps = ns; |
| std::cout << "100 years expressed as picoseconds = " << ps.count() << '\n'; |
| ps = ns / sat(1000); |
| std::cout << "0.1 years expressed as picoseconds = " << ps.count() << '\n'; |
| yr = years(sat(-200000000)); |
| std::cout << "200 million years ago encoded in years: " << yr.count() << '\n'; |
| days d = boost::chrono::duration_cast<days>(yr); |
| std::cout << "200 million years ago encoded in days: " << d.count() << '\n'; |
| millennium c = boost::chrono::duration_cast<millennium>(yr); |
| std::cout << "200 million years ago encoded in millennium: " << c.count() << '\n'; |
| std::cout << "Demonstrate \"uninitialized protection\" behavior:\n"; |
| seconds sec; |
| for (++sec; sec < seconds(sat(10)); ++sec) |
| ; |
| std::cout << sec.count() << '\n'; |
| std::cout << "\n"; |
| } |
| |
| void testStdUser() |
| { |
| std::cout << "***************\n"; |
| std::cout << "* testStdUser *\n"; |
| std::cout << "***************\n"; |
| using namespace boost::chrono; |
| hours hr = hours(100); |
| std::cout << "100 hours expressed as hours = " << hr.count() << '\n'; |
| nanoseconds ns = hr; |
| std::cout << "100 hours expressed as nanoseconds = " << ns.count() << '\n'; |
| ns += hr; |
| std::cout << "200 hours expressed as nanoseconds = " << ns.count() << '\n'; |
| ns += hr; |
| std::cout << "300 hours expressed as nanoseconds = " << ns.count() << '\n'; |
| // hr = ns; // does not compile |
| std::cout << "hr = ns; // does not compile\n"; |
| // hr * ns; // does not compile |
| std::cout << "hr * ns; // does not compile\n"; |
| duration<double> fs(2.5); |
| std::cout << "duration<double> has count() = " << fs.count() << '\n'; |
| // seconds sec = fs; // does not compile |
| std::cout << "seconds sec = duration<double> won't compile\n"; |
| seconds sec = duration_cast<seconds>(fs); |
| std::cout << "seconds has count() = " << sec.count() << '\n'; |
| std::cout << "\n"; |
| } |
| |
| |
| int main() |
| { |
| testStdUser(); |
| testUser2(); |
| return 0; |
| } |
| |