Подтвердить что ты не робот

С++ Как преобразовать std:: chrono:: time_point в long and back

Мне нужно преобразовать std::chrono::time_point в тип и из long (целое число 64 бит). Я начинаю работать с std::chrono...

Вот мой код:

int main ()
{
     std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now();

    auto epoch = now.time_since_epoch();
    auto value = std::chrono::duration_cast<std::chrono::milliseconds>(epoch);
    long duration = value.count();


    std::chrono::duration<long> dur(duration);

    std::chrono::time_point<std::chrono::system_clock> dt(dur);

    if (dt != now)
        std::cout << "Failure." << std::endl;
    else
        std::cout << "Success." << std::endl;
}

Этот код компилируется, но не показывает успеха.

Почему dt отличается от now в конце?

Что не хватает на этом коде?

4b9b3361

Ответ 1

std::chrono::time_point<std::chrono::system_clock> now = std::chrono::system_clock::now();

Это отличное место для auto:

auto now = std::chrono::system_clock::now();

Поскольку вы хотите трафик с точностью millisecond, было бы неплохо идти вперед и скрывать его в time_point:

auto now_ms = std::chrono::time_point_cast<std::chrono::milliseconds>(now);

now_ms - это time_point, основанный на system_clock, но с точностью milliseconds вместо любой точности вашей system_clock.

auto epoch = now_ms.time_since_epoch();

epoch теперь имеет тип std::chrono::milliseconds. И этот следующий оператор становится практически не-op (просто делает копию и не делает преобразования):

auto value = std::chrono::duration_cast<std::chrono::milliseconds>(epoch);

Здесь:

long duration = value.count();

В вашем и моем коде duration хранится число milliseconds с момента <<28 > .

Это:

std::chrono::duration<long> dur(duration);

Создает duration, представленный long, и точность seconds. Это эффективно reinterpret_cast milliseconds, содержащееся в value до seconds. Это логическая ошибка. Правильный код будет выглядеть так:

std::chrono::milliseconds dur(duration);

Эта строка:

std::chrono::time_point<std::chrono::system_clock> dt(dur);

создает time_point на основе system_clock, с возможностью удержания точности с собственной точностью system_clock (обычно более тонкой, чем миллисекунды). Однако значение времени выполнения будет правильно отражать то, что фиксируется целое число миллисекунд (при условии, что я исправляю тип dur).

Даже при коррекции этот тест будет (почти всегда) терпеть неудачу:

if (dt != now)

Поскольку dt содержит целое число milliseconds, но now содержит целое число тиков, более тонкое, чем a millisecond (например, microseconds или nanoseconds). Таким образом, только по редкой вероятности, что system_clock::now() вернул целое число milliseconds, пройдет тест.

Но вы можете вместо этого:

if (dt != now_ms)

И теперь вы надежно получите ожидаемый результат.

Объединяя все это:

int main ()
{
    auto now = std::chrono::system_clock::now();
    auto now_ms = std::chrono::time_point_cast<std::chrono::milliseconds>(now);

    auto value = now_ms.time_since_epoch();
    long duration = value.count();

    std::chrono::milliseconds dur(duration);

    std::chrono::time_point<std::chrono::system_clock> dt(dur);

    if (dt != now_ms)
        std::cout << "Failure." << std::endl;
    else
        std::cout << "Success." << std::endl;
}

Лично я нахожу все std::chrono слишком многословными, поэтому я бы назвал его как:

int main ()
{
    using namespace std::chrono;
    auto now = system_clock::now();
    auto now_ms = time_point_cast<milliseconds>(now);

    auto value = now_ms.time_since_epoch();
    long duration = value.count();

    milliseconds dur(duration);

    time_point<system_clock> dt(dur);

    if (dt != now_ms)
        std::cout << "Failure." << std::endl;
    else
        std::cout << "Success." << std::endl;
}

Что будет надежно выводиться:

Success.

Наконец, я рекомендую исключить временные сокращения, чтобы уменьшить преобразование кода между time_point и интегральным типом до минимума. Эти преобразования опасны, и тем меньше кода, который вы пишете, манипулируя голым интегральным типом, тем лучше:

int main ()
{
    using namespace std::chrono;
    // Get current time with precision of milliseconds
    auto now = time_point_cast<milliseconds>(system_clock::now());
    // sys_milliseconds is type time_point<system_clock, milliseconds>
    using sys_milliseconds = decltype(now);
    // Convert time_point to signed integral type
    auto integral_duration = now.time_since_epoch().count();
    // Convert signed integral type to time_point
    sys_milliseconds dt{milliseconds{integral_duration}};
    // test
    if (dt != now)
        std::cout << "Failure." << std::endl;
    else
        std::cout << "Success." << std::endl;
}

Основная опасность выше не интерпретирует integral_duration как milliseconds на обратном пути к time_point. Один из возможных способов уменьшить этот риск - написать:

    sys_milliseconds dt{sys_milliseconds::duration{integral_duration}};

Это снижает риск вплоть до того, что вы используете sys_milliseconds на выходе и в двух местах на обратном пути.

И еще один пример: предположим, что вы хотите преобразовать в интеграл и из него, который представляет любую продолжительность поддержки system_clock (микросекунды, 10 th микросекунд или наносекунды). Тогда вам не нужно беспокоиться о том, чтобы указать миллисекунды, как указано выше. Код упрощает:

int main ()
{
    using namespace std::chrono;
    // Get current time with native precision
    auto now = system_clock::now();
    // Convert time_point to signed integral type
    auto integral_duration = now.time_since_epoch().count();
    // Convert signed integral type to time_point
    system_clock::time_point dt{system_clock::duration{integral_duration}};
    // test
    if (dt != now)
        std::cout << "Failure." << std::endl;
    else
        std::cout << "Success." << std::endl;
}

Это работает, но если вы выполняете половину преобразования (до целого) на одной платформе, а другую половину (из интегральной) на другой платформе, вы рискуете, что system_clock::duration будет иметь разные префикс для двух преобразований.

Ответ 2

Я также хотел бы отметить, что есть два способа получить количество мс в момент времени. Я не уверен, какой из них лучше, я сравнивал их, и они оба имеют одинаковую производительность, поэтому я думаю, что это вопрос предпочтения. Возможно, Говард мог перезвонить:

auto now = system_clock::now();

//Cast the time point to ms, then get its duration, then get the duration count.
auto ms = time_point_cast<milliseconds>(now).time_since_epoch().count();

//Get the time point duration, then cast to ms, then get its count.
auto ms = duration_cast<milliseconds>(tpBid.time_since_epoch()).count();

Первый читается более четко в моем сознании, идущем слева направо.

Ответ 3

time_point objects поддерживают только арифметику с другими объектами time_point или duration.

Вам нужно будет преобразовать long в duration указанных единиц, тогда ваш код должен работать правильно.