Шрифт:
Интервал:
Закладка:
static pair<string, seconds> 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<chrono::steady_clock>. Разность между двумя такими значениями (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<double>;
using milliseconds = chrono::duration<double,
ratio_multiply<seconds::period, milli>>;
using microseconds = chrono::duration<double,
ratio_multiply<seconds::period, micro>>;
Секунды — самая простая в описании единица времени, поскольку можно воспользоваться