C++17 STL Стандартная библиотека шаблонов — страница 78 из 119

>;


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
— псевдоним типа