ошибка: преобразование из std :: chrono :: time_point с плавающей точкой в ​​нескалярный тип long int запрашивается - PullRequest
2 голосов
/ 20 июня 2019

У меня есть этот код, который пытается создать новую точку времени, добавив продолжительность:

// now
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
// duration
std::chrono::duration<float> duration(1.0f / 60.0f); // float in seconds
// now + duration
std::chrono::system_clock::time_point futureTime = now + duration;

Но это дает мне эту ошибку

error: conversion from 'std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<float, std::ratio<1, 1000000000> > >' to non-scalar type 'std::chrono::_V2::system_clock::time_point {aka std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<long int, std::ratio<1, 1000000000> > >}' requested
# it's over there in the distance --->

# ... again, with some manual formatting:
error:
    conversion from
    'std::chrono::time_point<std::chrono::_V2::system_clock,
                             std::chrono::duration<float, 
                                                   std::ratio<1, 1000000000> > >'
    to non-scalar type
    'std::chrono::_V2::system_clock::time_point
     {aka 
     std::chrono::time_point<std::chrono::_V2::system_clock,
                             std::chrono::duration<long int,
                                                   std::ratio<1, 1000000000> > >}'
    requested

После некоторого сильного прищурения в первом есть float, а во втором - long int. Таким образом, now (long int) + duration (float) дает time_point с некоторой внутренней длительностью с плавающей запятой, и я, вероятно, прошу его вставить обратно в стандартное представление long int.

Я бы хотел в конечном итоге передать эту временную точку на std::condition_variable::wait_until. Как я могу заставить преобразование в std::chrono::system_clock::time_point?

Какую точность я потеряю, если проиграю (т. Е. Хранит ли я миллисекунды, и в этом случае я потеряю часть своей 1/60)?

Если я не преобразую его, какой хороший короткий способ написать тип, который возвращает now + duration (да, я мог бы использовать auto, но, скажем, передать его функции)?

Ответы [ 2 ]

3 голосов
/ 20 июня 2019

Анализ, который вы провели в своем вопросе, в основном верен. И есть несколько хороших способов сделать эту работу. 1 , 2

Использование auto

// now
system_clock::time_point now = system_clock::now();
// duration
duration<float> duration(1.0f / 60.0f); // float in seconds
// now + duration
auto futureTime = now + duration;

Тип futureTime является time_point<system_clock, duration<float, nano>>. То есть это time_point на основе system_clock, в котором хранится nanoseconds, представленный float.

Этот тип может использоваться с std::condition_variable::wait_until.

Cast now + duration назад к system_clock::time_point:

// now
system_clock::time_point now = system_clock::now();
// duration
duration<float> duration(1.0f / 60.0f); // float in seconds
// now + duration
system_clock::time_point futureTime =
    time_point_cast<system_clock::duration>(now + duration);

time_point_cast<duration> - как вы разыгрываете time_point с. Вы должны явно указать тип длительности, которую вы хотите получить в результате time_point. Результирующий time_point будет по-прежнему основываться на тех же часах. Результат будет усечен (округлен до нуля) до следующего целого числа system_clock::duration (nanoseconds на этой платформе).

Этот тип может использоваться с std::condition_variable::wait_until.

Вокруг now + duration назад к system_clock::time_point:

system_clock::time_point futureTime = round<system_clock::duration>(now + duration);

Это похоже на предыдущее решение, за исключением округления до ближайшего интеграла nanoseconds и до четного nanoseconds на ничью. std::chrono::round доступно в C ++ 17 и более поздних версиях. Это даст результат не более 1ns, отличный от time_point_cast.

Если у вас нет C ++ 17, но вы все еще хотите использовать round, не стесняйтесь использовать этот .

Этот тип может использоваться с std::condition_variable::wait_until.

Представляет 1/60 в точности с интегральной арифметикой

// now
system_clock::time_point now = system_clock::now();
// duration
duration<int, std::ratio<1, 60>> duration{1}; // 1/60 of a second
// now + duration
auto futureTime = now + duration;

Обычно люди говорят:

Невозможно точно представить 1 / 60 секунду.

Но вы можете с <chrono>. :-) duration<int, std::ratio<1, 60>> считает в единицах 1 / 60 секунды, используя представление int. duration{1} равно 1 / 60 секунды. duration{2} составляет 2 / 60 секунды. И т.д.

auto действительно пригодится здесь. Тип futureTime: time_point<system_clock, duration<system_clock::rep, ratio<1, 3'000'000'000>>>. Или по-английски: A time_point на основе system_clock с использованием продолжительности с типом представления, который system_clock::time_point использует (обычно long long), и периодом 1 / 3 nanoseconds.

Оказывается, что 1 / 3 nanoseconds - это грубая точность, которая может точно представлять оба system_clock::period ( nanoseconds на этой платформе) и 1 / 60 секунды.

И <chrono> вычисляет все это автоматически. Все, что вам нужно сделать, чтобы воспользоваться этим - использовать auto.

Этот тип может использоваться с std::condition_variable::wait_until.

Можно также использовать duration<int, std::ratio<1, 60>> в сочетании с time_point_cast или round, как показано ранее, если вы хотите вернуться к system_clock::time_point.

на основе наносекунд.

Резюме

Что лучше? Вы должны будете решить это на основе других факторов в вашем приложении. Обычно лучше всего то, что делает остальную часть вашего кода более читабельной.

Но у вас есть варианты, и все они хорошие варианты. И все они будут работать с std::condition_variable::wait_until.

Когда вы изучаете свои варианты, если он не компилируется, это потому, что <chrono> перехватывает ваши ошибки во время компиляции (как и первый). Если он компилируется (и если вы не избежали системы типов с .count() или .time_since_epoch()), то он работает.


1 Ответы в этом вопросе предполагают, что system_clock::duration равно nanoseconds, как указано в вопросе. Это верно только для gcc и отличается в Windows и macOS.

2 Чтобы не потеряться в многословии, я пишу свои ответы, как будто есть:

using namespace std::chrono;

в игре. Это делает вещи , поэтому намного более читабельными.

0 голосов
/ 20 июня 2019

std::condition_variable::wait_until выглядит следующим образом: ( cppreference )

template< class Clock, class Duration >
std::cv_status
    wait_until( std::unique_lock<std::mutex>& lock,
                const std::chrono::time_point<Clock, Duration>& timeout_time );

template< class Clock, class Duration, class Pred >    
bool wait_until( std::unique_lock<std::mutex>& lock,
                 const std::chrono::time_point<Clock, Duration>& timeout_time,
                 Pred pred );

Вы можете передать ему time_point<C, D>.Это не должно быть system_clock::time_pointsystem_clock::time_point это просто time_point<system_clock>.

Точное представление часов определяется реализацией, но это должен быть целочисленный тип.Невозможно точно представить (1/60)s с целым числом как число и секундой как единицу.

now + duration, где now имеет тип time_point<C, D1>, а duration имеет типduration<R2, P2>, имеет тип time_point<C, std::common_type_t<duration<R1, P1>, D2>>.

...