C ++ Як перетворити std :: chrono :: time_point у long та назад


83

Мені потрібно перетворити 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в кінці?

Чого не вистачає цьому коду?


1
Я не надто знайомий з бібліотекою chrono, але я вважаю, що вам доведеться користуватися, std::chrono::duration<long,std::milli> durі навіть тоді ви можете отримати помилки округлення ( std::chrono::system_clockмабуть, роздільна здатність вища, ніж мілісекунди).
— MikeMB

@MikeMB усі годинники на досить новому обладнанні повинні мати десь приблизно мікросекундну / субмікросекундну точність. Дивіться статтю під назвою Три годинники . Дійсно, вікна, як правило, мають більш високу точність із system_clock(приблизно в 100 разів більшу), але навіть це, як правило, менше мікросекунди.
— Werlious

Відповіді:


164
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. І це наступне твердження стає по суті забороною (просто робить копію і не робить перетворення):

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

Тут:

long duration = value.count();

І в вашому, і в моєму коді durationміститься число з millisecondsчасів system_clock.

Це:

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

Створює durationпредставлене з a 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вміщує ціле число кліщів, менших за 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на шляху назад до a time_point. Одним із можливих способів зменшити цей ризик є написання:

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

Це зменшує ризик, просто переконуючись, що ви використовуєте sys_millisecondsна виході та в двох місцях на зворотному шляху.

І ще один приклад: припустимо, ви хочете перетворити на інтеграл і з нього, який представляє будь-яку system_clockпідтримку тривалості (мікросекунди, 10 - та мікросекунда або наносекунда). Тоді вам не доведеться турбуватися про те, щоб вказати мілісекунди, як зазначено вище. Код спрощений до:

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мати різні точності для двох перетворень.


1
@BrentNash: Обережно, я можу взяти вас за це. ;-) Я буду на Cppcon 2015 ( cppcon.org ) і буду говорити про це: howardhinnant.github.io/date_v2.html . Тема дуже тісно пов’язана з time_point_cast<milliseconds>(now)моєю відповіддю вище. Тільки тривалість скаковая: time_point_cast<days>(now).
— Говард Хіннант

Помилка в останньому коментарі: грубіша, а не курсерська. Хороша відповідь.
— Dirk Eddelbuettel

2
Проблема англійської мови полягає в тому, що вона не піддається синтаксичному аналізу з компілятором C ++, і компілятор покращує перевірку орфографії. :-)
— Говард Хіннант

4
Це дуже корисно, хоча, я думаю, це було б трохи краще, якщо б ви не всюди використовували "авто". Просто для таких ледачих людей, як я, які хотіли б побачити, якими типами маніпулюють, не маючи потреби шукати деінде. Спасибі купу @HowardHinnant
— MarkoPaulo

7

Я б також зазначив, що існує два способи отримати кількість мс у точці часу. Я не впевнений, який з них краще, я порівняв їх, і вони обидва мають однакову ефективність, тому, мабуть, це питання переваг. Можливо, Говард міг би звучати:

auto now = system_clock::now();

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

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

Перший читається в моїй свідомості чіткіше, рухаючись зліва направо.


1

як один рядок:

long value_ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now()).time_since_epoch()).count();

0

time_pointоб'єкти підтримують лише арифметику з іншими time_pointабо durationоб'єктами.

Вам потрібно буде перетворити ваш longна durationвказані одиниці, тоді ваш код повинен працювати коректно.


Чи можете ви опублікувати приклад?
— Мендес,

Обидва надані посилання мають розділ "Приклад" внизу.
— Містер Лама,

Я зробив його компіляцією, але є деяка логічна помилка ... Я відредагував оригінальний код ...
— Мендес,

1
Відповіді було б корисно включити приклади. Посилання гинуть або змінюються. З огляду на це, я не впевнений, чому це було проти.
— TankorSmash
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.