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.