1 //  saturating.cpp  ----------------------------------------------------------//
2 
3 //  Copyright 2008 Howard Hinnant
4 //  Copyright 2008 Beman Dawes
5 //  Copyright 2009 Vicente J. Botet Escriba
6 
7 //  Distributed under the Boost Software License, Version 1.0.
8 //  See http://www.boost.org/LICENSE_1_0.txt
9 
10 /*
11 This code was extracted by Vicente J. Botet Escriba from Beman Dawes time2_demo.cpp which
12 was derived by Beman Dawes from Howard Hinnant's time2_demo prototype.
13 Many thanks to Howard for making his code available under the Boost license.
14 The original code was modified to conform to Boost conventions and to section
15 20.9 Time utilities [time] of the C++ committee's working paper N2798.
16 See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2798.pdf.
17 
18 time2_demo contained this comment:
19 
20     Much thanks to Andrei Alexandrescu,
21                    Walter Brown,
22                    Peter Dimov,
23                    Jeff Garland,
24                    Terry Golubiewski,
25                    Daniel Krugler,
26                    Anthony Williams.
27 */
28 
29 #define _CRT_SECURE_NO_WARNINGS  // disable VC++ foolishness
30 
31 #include <boost/chrono/chrono.hpp>
32 #include <boost/type_traits.hpp>
33 
34 #include <iostream>
35 
36 //////////////////////////////////////////////////////////
37 //////////////////// User2 Example ///////////////////////
38 //////////////////////////////////////////////////////////
39 
40 // Demonstrate User2:
41 // A "saturating" signed integral type  is developed.  This type has +/- infinity and a nan
42 // (like IEEE floating point) but otherwise obeys signed integral arithmetic.
43 // This class is subsequently used as the rep in boost::chrono::duration to demonstrate a
44 // duration class that does not silently ignore overflow.
45 #include <ostream>
46 #include <stdexcept>
47 #include <climits>
48 
49 namespace User2
50 {
51 
52 template <class I>
53 class saturate
54 {
55 public:
56     typedef I int_type;
57 
58     static const int_type nan = int_type(int_type(1) << (sizeof(int_type) * CHAR_BIT - 1));
59     static const int_type neg_inf = nan + 1;
60     static const int_type pos_inf = -neg_inf;
61 private:
62     int_type i_;
63 
64 //     static_assert(std::is_integral<int_type>::value && std::is_signed<int_type>::value,
65 //                   "saturate only accepts signed integral types");
66 //     static_assert(nan == -nan && neg_inf < pos_inf,
67 //                   "saturate assumes two's complement hardware for signed integrals");
68 
69 public:
saturate()70     saturate() : i_(nan) {}
saturate(int_type i)71     explicit saturate(int_type i) : i_(i) {}
72     // explicit
73     operator int_type() const;
74 
75     saturate& operator+=(saturate x);
operator -=(saturate x)76     saturate& operator-=(saturate x) {return *this += -x;}
77     saturate& operator*=(saturate x);
78     saturate& operator/=(saturate x);
79     saturate& operator%=(saturate x);
80 
operator -() const81     saturate  operator- () const {return saturate(-i_);}
operator ++()82     saturate& operator++()       {*this += saturate(int_type(1)); return *this;}
operator ++(int)83     saturate  operator++(int)    {saturate tmp(*this); ++(*this); return tmp;}
operator --()84     saturate& operator--()       {*this -= saturate(int_type(1)); return *this;}
operator --(int)85     saturate  operator--(int)    {saturate tmp(*this); --(*this); return tmp;}
86 
operator +(saturate x,saturate y)87     friend saturate operator+(saturate x, saturate y) {return x += y;}
operator -(saturate x,saturate y)88     friend saturate operator-(saturate x, saturate y) {return x -= y;}
operator *(saturate x,saturate y)89     friend saturate operator*(saturate x, saturate y) {return x *= y;}
operator /(saturate x,saturate y)90     friend saturate operator/(saturate x, saturate y) {return x /= y;}
operator %(saturate x,saturate y)91     friend saturate operator%(saturate x, saturate y) {return x %= y;}
92 
operator ==(saturate x,saturate y)93     friend bool operator==(saturate x, saturate y)
94     {
95         if (x.i_ == nan || y.i_ == nan)
96             return false;
97         return x.i_ == y.i_;
98     }
99 
operator !=(saturate x,saturate y)100     friend bool operator!=(saturate x, saturate y) {return !(x == y);}
101 
operator <(saturate x,saturate y)102     friend bool operator<(saturate x, saturate y)
103     {
104         if (x.i_ == nan || y.i_ == nan)
105             return false;
106         return x.i_ < y.i_;
107     }
108 
operator <=(saturate x,saturate y)109     friend bool operator<=(saturate x, saturate y)
110     {
111         if (x.i_ == nan || y.i_ == nan)
112             return false;
113         return x.i_ <= y.i_;
114     }
115 
operator >(saturate x,saturate y)116     friend bool operator>(saturate x, saturate y)
117     {
118         if (x.i_ == nan || y.i_ == nan)
119             return false;
120         return x.i_ > y.i_;
121     }
122 
operator >=(saturate x,saturate y)123     friend bool operator>=(saturate x, saturate y)
124     {
125         if (x.i_ == nan || y.i_ == nan)
126             return false;
127         return x.i_ >= y.i_;
128     }
129 
operator <<(std::ostream & os,saturate s)130     friend std::ostream& operator<<(std::ostream& os, saturate s)
131     {
132         switch (s.i_)
133         {
134         case pos_inf:
135             return os << "inf";
136         case nan:
137             return os << "nan";
138         case neg_inf:
139             return os << "-inf";
140         };
141         return os << s.i_;
142     }
143 };
144 
145 template <class I>
operator I() const146 saturate<I>::operator I() const
147 {
148     switch (i_)
149     {
150     case nan:
151     case neg_inf:
152     case pos_inf:
153         throw std::out_of_range("saturate special value can not convert to int_type");
154     }
155     return i_;
156 }
157 
158 template <class I>
159 saturate<I>&
operator +=(saturate x)160 saturate<I>::operator+=(saturate x)
161 {
162     switch (i_)
163     {
164     case pos_inf:
165         switch (x.i_)
166         {
167         case neg_inf:
168         case nan:
169             i_ = nan;
170         }
171         return *this;
172     case nan:
173         return *this;
174     case neg_inf:
175         switch (x.i_)
176         {
177         case pos_inf:
178         case nan:
179             i_ = nan;
180         }
181         return *this;
182     }
183     switch (x.i_)
184     {
185     case pos_inf:
186     case neg_inf:
187     case nan:
188         i_ = x.i_;
189         return *this;
190     }
191     if (x.i_ >= 0)
192     {
193         if (i_ < pos_inf - x.i_)
194             i_ += x.i_;
195         else
196             i_ = pos_inf;
197         return *this;
198     }
199     if (i_ > neg_inf - x.i_)
200         i_ += x.i_;
201     else
202         i_ = neg_inf;
203     return *this;
204 }
205 
206 template <class I>
207 saturate<I>&
operator *=(saturate x)208 saturate<I>::operator*=(saturate x)
209 {
210     switch (i_)
211     {
212     case 0:
213         switch (x.i_)
214         {
215         case pos_inf:
216         case neg_inf:
217         case nan:
218             i_ = nan;
219         }
220         return *this;
221     case pos_inf:
222         switch (x.i_)
223         {
224         case nan:
225         case 0:
226             i_ = nan;
227             return *this;
228         }
229         if (x.i_ < 0)
230             i_ = neg_inf;
231         return *this;
232     case nan:
233         return *this;
234     case neg_inf:
235         switch (x.i_)
236         {
237         case nan:
238         case 0:
239             i_ = nan;
240             return *this;
241         }
242         if (x.i_ < 0)
243             i_ = pos_inf;
244         return *this;
245     }
246     switch (x.i_)
247     {
248     case 0:
249         i_ = 0;
250         return *this;
251     case nan:
252         i_ = nan;
253         return *this;
254     case pos_inf:
255         if (i_ < 0)
256             i_ = neg_inf;
257         else
258             i_ = pos_inf;
259         return *this;
260     case neg_inf:
261         if (i_ < 0)
262             i_ = pos_inf;
263         else
264             i_ = neg_inf;
265         return *this;
266     }
267     int s = (i_ < 0 ? -1 : 1) * (x.i_ < 0 ? -1 : 1);
268     i_ = i_ < 0 ? -i_ : i_;
269     int_type x_i_ = x.i_ < 0 ? -x.i_ : x.i_;
270     if (i_ <= pos_inf / x_i_)
271         i_ *= x_i_;
272     else
273         i_ = pos_inf;
274     i_ *= s;
275     return *this;
276 }
277 
278 template <class I>
279 saturate<I>&
operator /=(saturate x)280 saturate<I>::operator/=(saturate x)
281 {
282     switch (x.i_)
283     {
284     case pos_inf:
285     case neg_inf:
286         switch (i_)
287         {
288         case pos_inf:
289         case neg_inf:
290         case nan:
291             i_ = nan;
292             break;
293         default:
294             i_ = 0;
295             break;
296         }
297         return *this;
298     case nan:
299         i_ = nan;
300         return *this;
301     case 0:
302         switch (i_)
303         {
304         case pos_inf:
305         case neg_inf:
306         case nan:
307             return *this;
308         case 0:
309             i_ = nan;
310             return *this;
311         }
312         if (i_ > 0)
313             i_ = pos_inf;
314         else
315             i_ = neg_inf;
316         return *this;
317     }
318     switch (i_)
319     {
320     case 0:
321     case nan:
322         return *this;
323     case pos_inf:
324     case neg_inf:
325         if (x.i_ < 0)
326             i_ = -i_;
327         return *this;
328     }
329     i_ /= x.i_;
330     return *this;
331 }
332 
333 template <class I>
334 saturate<I>&
operator %=(saturate x)335 saturate<I>::operator%=(saturate x)
336 {
337 //    *this -= *this / x * x;  // definition
338     switch (x.i_)
339     {
340     case nan:
341     case neg_inf:
342     case 0:
343     case pos_inf:
344         i_ = nan;
345         return *this;
346     }
347     switch (i_)
348     {
349     case neg_inf:
350     case pos_inf:
351         i_ = nan;
352     case nan:
353         return *this;
354     }
355     i_ %= x.i_;
356     return *this;
357 }
358 
359 // Demo overflow-safe integral durations ranging from picoseconds resolution to millennium resolution
360 typedef boost::chrono::duration<saturate<long long>, boost::pico                 > picoseconds;
361 typedef boost::chrono::duration<saturate<long long>, boost::nano                 > nanoseconds;
362 typedef boost::chrono::duration<saturate<long long>, boost::micro                > microseconds;
363 typedef boost::chrono::duration<saturate<long long>, boost::milli                > milliseconds;
364 typedef boost::chrono::duration<saturate<long long>                            > seconds;
365 typedef boost::chrono::duration<saturate<long long>, boost::ratio<         60LL> > minutes;
366 typedef boost::chrono::duration<saturate<long long>, boost::ratio<       3600LL> > hours;
367 typedef boost::chrono::duration<saturate<long long>, boost::ratio<      86400LL> > days;
368 typedef boost::chrono::duration<saturate<long long>, boost::ratio<   31556952LL> > years;
369 typedef boost::chrono::duration<saturate<long long>, boost::ratio<31556952000LL> > millennium;
370 
371 }  // User2
372 
373 // Demonstrate custom promotion rules (needed only if there are no implicit conversions)
374 namespace User2 { namespace detail {
375 
376 template <class T1, class T2, bool = boost::is_integral<T1>::value>
377 struct promote_helper;
378 
379 template <class T1, class T2>
380 struct promote_helper<T1, saturate<T2>, true>  // integral
381 {
382     typedef typename boost::common_type<T1, T2>::type rep;
383     typedef User2::saturate<rep> type;
384 };
385 
386 template <class T1, class T2>
387 struct promote_helper<T1, saturate<T2>, false>  // floating
388 {
389     typedef T1 type;
390 };
391 
392 } }
393 
394 namespace boost
395 {
396 
397 template <class T1, class T2>
398 struct common_type<User2::saturate<T1>, User2::saturate<T2> >
399 {
400     typedef typename common_type<T1, T2>::type rep;
401     typedef User2::saturate<rep> type;
402 };
403 
404 template <class T1, class T2>
405 struct common_type<T1, User2::saturate<T2> >
406     : User2::detail::promote_helper<T1, User2::saturate<T2> > {};
407 
408 template <class T1, class T2>
409 struct common_type<User2::saturate<T1>, T2>
410     : User2::detail::promote_helper<T2, User2::saturate<T1> > {};
411 
412 
413 // Demonstrate specialization of duration_values:
414 
415 namespace chrono {
416 
417 template <class I>
418 struct duration_values<User2::saturate<I> >
419 {
420     typedef User2::saturate<I> Rep;
421 public:
zeroboost::chrono::duration_values422     static Rep zero() {return Rep(0);}
BOOST_PREVENT_MACRO_SUBSTITUTIONboost::chrono::duration_values423     static Rep max BOOST_PREVENT_MACRO_SUBSTITUTION ()  {return Rep(Rep::pos_inf-1);}
BOOST_PREVENT_MACRO_SUBSTITUTIONboost::chrono::duration_values424     static Rep min BOOST_PREVENT_MACRO_SUBSTITUTION ()  {return -(max)();}
425 };
426 
427 }  // namespace chrono
428 
429 }  // namespace boost
430 
431 #include <iostream>
432 
testUser2()433 void testUser2()
434 {
435     std::cout << "*************\n";
436     std::cout << "* testUser2 *\n";
437     std::cout << "*************\n";
438     using namespace User2;
439     typedef seconds::rep sat;
440     years yr(sat(100));
441     std::cout << "100 years expressed as years = " << yr.count() << '\n';
442     nanoseconds ns = yr;
443     std::cout << "100 years expressed as nanoseconds = " << ns.count() << '\n';
444     ns += yr;
445     std::cout << "200 years expressed as nanoseconds = " << ns.count() << '\n';
446     ns += yr;
447     std::cout << "300 years expressed as nanoseconds = " << ns.count() << '\n';
448 //    yr = ns;  // does not compile
449     std::cout << "yr = ns;  // does not compile\n";
450 //    picoseconds ps1 = yr;  // does not compile, compile-time overflow in ratio arithmetic
451     std::cout << "ps = yr;  // does not compile\n";
452     ns = yr;
453     picoseconds ps = ns;
454     std::cout << "100 years expressed as picoseconds = " << ps.count() << '\n';
455     ps = ns / sat(1000);
456     std::cout << "0.1 years expressed as picoseconds = " << ps.count() << '\n';
457     yr = years(sat(-200000000));
458     std::cout << "200 million years ago encoded in years: " << yr.count() << '\n';
459     days d = boost::chrono::duration_cast<days>(yr);
460     std::cout << "200 million years ago encoded in days: " << d.count() << '\n';
461     millennium c = boost::chrono::duration_cast<millennium>(yr);
462     std::cout << "200 million years ago encoded in millennium: " << c.count() << '\n';
463     std::cout << "Demonstrate \"uninitialized protection\" behavior:\n";
464     seconds sec;
465     for (++sec; sec < seconds(sat(10)); ++sec)
466         ;
467     std::cout << sec.count() << '\n';
468     std::cout << "\n";
469 }
470 
testStdUser()471 void testStdUser()
472 {
473     std::cout << "***************\n";
474     std::cout << "* testStdUser *\n";
475     std::cout << "***************\n";
476     using namespace boost::chrono;
477     hours hr = hours(100);
478     std::cout << "100 hours expressed as hours = " << hr.count() << '\n';
479     nanoseconds ns = hr;
480     std::cout << "100 hours expressed as nanoseconds = " << ns.count() << '\n';
481     ns += hr;
482     std::cout << "200 hours expressed as nanoseconds = " << ns.count() << '\n';
483     ns += hr;
484     std::cout << "300 hours expressed as nanoseconds = " << ns.count() << '\n';
485 //    hr = ns;  // does not compile
486     std::cout << "hr = ns;  // does not compile\n";
487 //    hr * ns;  // does not compile
488     std::cout << "hr * ns;  // does not compile\n";
489     duration<double> fs(2.5);
490     std::cout << "duration<double> has count() = " << fs.count() << '\n';
491 //    seconds sec = fs;  // does not compile
492     std::cout << "seconds sec = duration<double> won't compile\n";
493     seconds sec = duration_cast<seconds>(fs);
494     std::cout << "seconds has count() = " << sec.count() << '\n';
495     std::cout << "\n";
496 }
497 
498 
main()499 int main()
500 {
501     testStdUser();
502     testUser2();
503     return 0;
504 }
505 
506