>;
5. Теперь реализуем функцию, которая считает входную строку, отправленную пользователем, и определит, сколько времени ему потребовалось для ее ввода. Она не принимает никаких аргументов и возвращает строку, введенную пользователем, а также время, которое ему потребовалось на ввод, упакованные в одну пару:
static pair get_input()
{
string s;
6. Нужно получить время, в которое пользователь начал вводить строку, а также время, в которое он это делать закончил. Данная операция выглядит следующим образом:
const auto tic (chrono::steady_clock::now());
7. Сейчас мы получим данные от пользователя. Если эта операция пройдет безуспешно, то просто вернем кортеж, инициализированный значениями по умолчанию. Вызывающая сторона получит пустую строку:
if (!(cin >> s)) {
return {{}, {}};
}
8. В случае успеха продолжим работу, сделав еще один снимок текущего времени. Далее вернем входную строку и разницу между временными точками. Обратите внимание: обе временные точки выражены в абсолютном виде, но, вычислив их разность, мы получаем длительность:
const auto toc (chrono::steady_clock::now());
return {s, toc - tic};
}
9. Теперь реализуем саму программу. Запустим цикл, который будет работать до тех пор, пока пользователь не введет корректную строку. На каждом шаге цикла мы просим пользователя ввести строку
"C++17"
, а затем вызываем функцию get_input
:
int main()
{
while (true) {
cout << "Please type the word \"C++17\" as"
" fast as you can.\n> ";
const auto [user_input, diff] = get_input();
10. Далее проверим входные данные. Если они пусты, то интерпретируем это как запрос на завершение программы:
if (user_input == "") { break; }
11. Если пользователь корректно введет строку
"C++17"
, то поздравим его, а затем выведем время, которое ему потребовалось на данное действие. Метод diff.count()
возвращает количество секунд в качестве числа с плавающей точкой. Используй мы оригинальный тип duration STL seconds
, получили бы округленное целочисленное значение, а не дробное. Передавая конструктору milliseconds
или microseconds
нашу переменную diff
перед вызовом count()
, получаем то же значение, преобразованное в другую единицу измерения:
if (user_input == "C++17") {
cout << "Bravo. You did it in:\n"
<< fixed << setprecision(2)
<< setw(12) << diff.count()
<< " seconds.\n"
<< setw(12) << milliseconds(diff).count()
<< " milliseconds.\n"
<< setw(12) << microseconds(diff).count()
<< " microseconds.\n";
break;
12. Если пользователь сделал опечатку, то позволим ему повторить попытку:
} else {
cout << "Sorry, your input does not match."
" You may try again.\n";
}
}
}
13. Компиляция и запуск программы дадут следующий результат. Сначала при наличии опечаток программа попросит пользователя ввести корректное слово. После этого она отобразит время, которое потребовалось на то, чтобы ввести его, в трех единицах измерения.
$ ./ratio_conversion
Please type the word "C++17" as fast as you can.
> c+17
Sorry, your input does not match. You may try again.
Please type the word "C++17" as fast as you can.
> C++17
Bravo. You did it in:
1.48 seconds.
1480.10 milliseconds.
1480099.00 microseconds.
Как это работает
Несмотря на то что этот раздел посвящен выполнению преобразований между разными единицами измерения времени, сначала нужно выбрать один из трех доступных объектов часов. Как правило, в пространстве имен
std::chrono
можно выбрать между system_clock
, steady_clock
и high_resolution_clock
. Чем они отличаются? Взглянем на их описание (табл. 8.1).Поскольку мы определяли продолжительность промежутка времени между двумя абсолютными точками во времени (они хранятся в переменных
tic
и toc
), нам не нужно знать, были ли эти точки искажены глобально. Даже если часы спешат или опаздывают на 112 лет 5 часов 10 минут и 1 секунду (или другое значение), это не отражается на разности между ними. Единственное, что важно, — после того, как мы сохраняем временную точку tic
, и до того, как сохраняем временную точку toc
, для часов нельзя выполнить микронастройку (что случается время от времени во многих системах), поскольку это исказит измерение. Согласно данным требованиям, оптимальным выбором является steady_clock
. Их реализация может быть основана на счетчике временных меток процессора, который всегда монотонно увеличивается с момента запуска системы.О’кей, теперь, когда мы выбрали правильный объект
time
, можем сохранить временные точки с помощью функции chrono::steady_clock::now()
. Функция now
возвращает значение типа chrono::time_point
. Разность между двумя такими значениями (toc–tic
) является временным промежутком, или продолжительностью, имеющей тип chrono::duration
.Поскольку данный тип является основным для текущего раздела, все немного усложняется. Рассмотрим интерфейс шаблонного типа
duration
более пристально:
template<
class Rep,
class Period = std::ratio<1>
> class duration;
Можно изменить значения параметров
Rep
и Period
. Значение параметра Rep объяснить легко: это всего лишь численный тип переменной, который используется для сохранения значения времени. Для существующих в STL единиц измерения времени таковым обычно выступает тип long long int
. В данном примере мы выбрали тип double
и благодаря этому можем сохранять по умолчанию значения в секундах, а затем преобразовывать их в милли- или микросекунды. Если у нас есть промежуток времени, равный 1.2345
секунды и имеющий тип chrono::seconds
, то значение будет округлено до одной целой секунды. Таким образом, нужно сохранить разность между переменными tic
и toc
в переменной типа chrono::microseconds
, а затем преобразовать его в менее точные единицы. Из-за выбора типа double
для Rep
можно выполнять преобразование к более и менее точным единицам и терять минимальный объем точности, что не влияет на наш пример.Мы использовали
Rep = double
для всех единиц измерения времени, поэтому они отличаются значением параметра Period
:
using seconds = chrono::duration;
using milliseconds = chrono::duration
ratio_multiply>;
using microseconds = chrono::duration
ratio_multiply>;
Секунды — самая простая в описании единица времени, поскольку можно воспользоваться конструкцией
Period = ratio<1>
, другие же придется подстраивать. Поскольку миллисекунда — одна тысячная секунды, мы умножим seconds::period
(который представляет собой всего лишь функцию-геттер для параметра Period
) на milli
— псевдоним типа