Working with time as with a dimensionless quantity can lead to misunderstandings and errors in converting temporary units of measurement:
- Listen, you do not remember, we pass seconds or milliseconds to sleep?
- Damn, it turns out I have 360 seconds in an hour, I missed zero.
To avoid such errors, the chrono library (namespace std :: chrono) is provided. It was added in C ++ 11 and refined in later standards. Now everything is logical:
using namespace std::chrono; int find_answer_to_the_ultimate_question_of_life() { // std::this_thread::sleep_for(5s); //5 return 42; } std::future<int> f = std::async(find_answer_to_the_ultimate_question_of_life); // 2.5 if (f.wait_for(2500ms) == std::future_status::ready) std::cout << "Answer is: " << f.get() << "\n"; else std::cout << "Can't wait anymore\n";
The library implements the following concepts:
duration
;time_point
;clock
.std :: ratio - template class that implements compile-time ordinary fraction (m / n). It does not belong to chrono, but is actively used by this library, therefore, first of all, we will get acquainted with it, so that it does not cause further questions.
template< std::intmax_t Num, // std::intmax_t Denom = 1 // > class ratio;
It is important that the numerator and denominator - constexpr template parameters. This allows the type to be built at compile time. This class is auxiliary (purely static, helper class), and generally speaking, not intended for mathematical calculations. It is needed to effectively convert units of measurement. For example, we want to work with different distance units:
template<class _Ratio> class Length { double length_; public: explicit Length(double length) : length_(length) { } double length() const { return length_; } }; Length<Mm> len1(127.0); Length<Inches> len2(5.0); Length<Mm> len3 = len1 + len2;
Let the millimeter be the base unit, then:
using Mm = std::ratio<1>; // == 1 // , : using Inches = std::ratio<254, 10>; using Metre = std::ratio<1000, 1>;
In the constructor, it was possible to convert to the base unit. But, more correctly, only where this transformation is needed. Because meters to millimeters can be converted without fear of loss when rounding, which is not the opposite.
In connection with the foregoing, just to complete the example, I cite not the most successful implementation of the addition operation, but a simple one:
template<class _Ratio1, class _Ratio2> Length<Mm> operator+(const Length<_Ratio1> &left, const Length<_Ratio2> &right) { double len = left.length() / _Ratio1::den * _Ratio1::num + right.length() / _Ratio2::den * _Ratio2::num; return Length<Mm>((int)len); }
It would be correct to receive meters when adding meters and kilometers.
The template class std :: chrono :: duration is a type of time interval. The time interval in chrono is a certain number of periods (in the original tick period). This number is characterized by the type, for example int64_t
or float
. The duration of the period is measured in seconds and is represented as a natural fraction using the std :: ratio.
Some popular intervals are already defined in the library. Types may vary slightly in different implementations.
using nanoseconds = duration<long long, nano>; using microseconds = duration<long long, micro>; using milliseconds = duration<long long, milli>; using seconds = duration<long long>; using minutes = duration<int, ratio<60> >; using hours = duration<int, ratio<3600> >; // nano, micro, milli: using nano = ratio<1, 1000000000>; using micro = ratio<1, 1000000>; using milli = ratio<1, 1000>;
But you can define your own:
using namespace std::chrono; //3- using Hourglass = duration<long, std::ratio<180>>; // using Hourglass = duration<long, std::ratio_multiply<std::ratio<3>, minutes::period>>; // 2.75 using MyTimeUnit = duration<long, std::ratio<11, 4>>; // . using fseconds = duration<float>; // - using seconds16 = duration<uint16_t>;
Now how to work with them. Implicit initialization is prohibited:
seconds s = 5; // void foo(minutes); foo(42); //
Only explicit:
seconds s{8}; void foo(minutes); foo(minutes{42});
By the way, why use curly braces can read, for example, here . In short: to avoid implicit conversion of integral types with losses. I will add another case when T x(F());
instead of initializing x, it is interpreted as a declaration of a function that takes a pointer to a function of type F(*)()
and returns T
Solution: T x{F()};
or T x((F()));
.
C ++ 14 adds custom literals for basic units:
seconds s = 4min; void foo(minutes); foo(42min);
You can add, subtract and compare:
seconds time1 = 5min + 17s; minutes time2 = 2h - 15min; bool less = 59s < 1min;
As in the example above, you can implicitly convert hours to minutes, minutes to seconds, seconds to milliseconds, etc., but not vice versa:
minutes time3 = 20s; // seconds time4 = 2s + 500ms; //
In general, implicit conversion for integer types is allowed if the ratio of periods is an integer:
//(20/15) / (1/3) = 4. ! duration<long, std::ratio<1, 3>> t1 = duration<long, std::ratio<20, 15>>{ 1 };
Otherwise, there are 2 ways: rounding and conversion to float-type.
// - minutes m1 = duration_cast<minutes>(-100s); //-1m //C++17. minutes m2 = round<minutes>(-100s); //-2m //C++17. minutes m3 = ceil<minutes>(-100s); //-1m //C++17. minutes m4 = floor<minutes>(-100s); //-2m
The second option:
using fminutes = duration<float, minutes::period>; fminutes m = -100s;
Suppose you have a redundant representation of the number of seconds with the type uint64_t. OK:
using seconds16 = duration<uint16_t, seconds::period>; seconds16 s = 15s;
But you still fear overflow. You can use the class from the library to work safely with numbers. There is no such standard in the standard (only a proposal), but there are third-party implementations. Also in VS, we use it:
#include <safeint.h> using sint = msl::utilities::SafeInt<uint16_t>; using safe_seconds16 = duration<sint, seconds::period>; safe_seconds16 ss = 60000s; try { ss += 10000s; } catch (msl::utilities::SafeIntException e) { // };
To display the interval value on the screen or in a file, you need to use count ():
seconds s = 15s; std::cout << s.count() << "s\n";
But do not use count for internal transformations!
The class time_point is intended to represent moments of time. The moment of time can be characterized as a time interval measured on any timer, starting from a certain point of reference. For example, if you are cooking soup using a stopwatch, your time points can be represented as follows:
0 : 420 : 1300 :
And if on the minute hand of a wall clock, then the same points in time can be:
17 : 24 : 39 :
So the class itself:
template< class Clock, class Duration = typename Clock::duration > class time_point;
The type of time interval is already familiar to us, now let's move on to the Clock timer. The library has 3 timers:
Clock has a static variable is_steady
, by which you can find out if the timer is monotonous. Clock also has a now function, which returns the current time in the form of time_point. By itself, the object of the class time_point
not very interesting, since the moment of its origin is not specific and makes little sense. But you can add time intervals to it and compare it with other points in time:
time_point<steady_clock> start = steady_clock::now(); // steady_clock::time_point start = steady_clock::now(); // auto start = steady_clock::now(); foo(); if (steady_clock::now() < start + 1s) std::cout << "Less than a second!\n";
time_point
cannot be added to time_point
, but you can subtract, which is useful for time tracking:
auto start = steady_clock::now(); foo(); auto end = steady_clock::now(); auto elapsed = duration_cast<milliseconds>(end - start);
To get the time interval that has passed since the beginning of the countdown, you can call time_since_epoch
:
auto now = system_clock::now(); system_clock::duration tse = now.time_since_epoch();
The time_point
to a number, for example, for serialization or display, can be done through the time_t C-type:
auto now = system_clock::now(); time_t now_t = system_clock::to_time_t(now); auto now2 = system_clock::from_time_t(now_t);
The most common question: how to display the time and date in a readable form. With the help of chrono in any way. You can play with time_t or use another library from the chrono developer.
Source: https://habr.com/ru/post/324984/
All Articles