C++ comment convertir un std:: chrono:: Time point en long et retour

j'ai besoin de convertir std::chrono::time_point et d'un long type (entier 64 bits). Im commencer le travail avec std::chrono ...

Voici mon code:

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;
}

Ce code compile, mais ne montre pas de succès.

Pourquoi dt différent now à la fin?

Ce qui manque sur ce code?

36
demandé sur Bruno Bieri 2015-07-06 23:58:21

3 réponses

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

C'est un endroit idéal pour les auto:

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

puisque vous voulez faire du trafic à millisecond précision, il serait bon d'aller de l'avant et dissimulée à l'intérieur du time_point:

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

now_ms est un time_point, basé sur system_clock, mais avec la précision d' milliseconds au lieu de n'importe quelle précision votre system_clock a.

auto epoch = now_ms.time_since_epoch();

epoch a maintenant le type std::chrono::milliseconds. Et cette déclaration suivante devient essentiellement un non-op (fait simplement une copie et ne pas faire une conversion):

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

Ici:

long duration = value.count();

Dans les deux de vous et de mon code, duration contient le nombre de milliseconds depuis l'époque de system_clock.

Ceci:

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

Crée un duration représenté par un long, et une précision de seconds. Ceci effectivement reinterpret_cast s le milliseconds tenue valueseconds. C'est une erreur de logique. Le code correct ressemblerait à:

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

ce ligne:

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

crée un time_point en fonction de system_clock, avec la capacité de tenir une précision à l' system_clock ' s Précision native (typiquement plus fine que millisecondes). Cependant, la valeur de temps d'exécution reflétera correctement qu'un nombre intégral de millisecondes est retenu (en supposant que ma correction sur le type de dur).

même avec la correction, ce test échouera (presque toujours) cependant:

if (dt != now)

Parce que dt détient une partie intégrante nombre de milliseconds, mais now contient un nombre entier de tiques fines qu'un millisecond (e.g. microseconds ou nanoseconds). Ce n'est donc que par une rare chance que system_clock::now() renvoie un nombre intégral de milliseconds le test passe.

mais vous pouvez à la place:

if (dt != now_ms)

et vous obtiendrez maintenant un résultat fiable.

Mettre tous ensemble:

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;
}

Personnellement, je trouve tous les std::chrono trop verbeux et donc je coderais comme:

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;
}

qui produira de façon fiable:

Success.

enfin, je recommande d'éliminer les temporalités pour réduire la conversion du code entre time_point et de type intégral au minimum. Ces conversions sont dangereuses, et donc le moins de code que vous écrivez en manipulant le type intégral nu le mieux:

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;
}

Le principal danger ci-dessus est interprétation integral_durationmilliseconds sur le chemin du retour à un time_point. Une façon possible de d'atténuer ce risque est d'écrire:

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

Cela réduit le risque jusqu'à faire assurez-vous d'utiliser sys_milliseconds sur le chemin, et dans les deux endroits sur le chemin du retour.

et un autre exemple: disons que vous voulez convertir en et à partir d'une intégrale qui représente n'importe quelle durée system_clock supports (microsecondes, 10 th de microsecondes ou nanosecondes). Alors vous n'avez pas à vous soucier de spécifier des millisecondes comme ci-dessus. Le le code simplifie à:

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;
}

Cela fonctionne, mais si vous exécutez la moitié de la conversion (intégrale) sur une plate-forme et l'autre moitié (à partir de l'intégrale) sur une autre plate-forme, vous courez le risque que system_clock::duration aura des précisions différentes pour les deux conversions.

83
répondu Howard Hinnant 2017-01-12 11:51:27

je voudrais également noter qu'il y a deux façons d'obtenir le nombre de ms dans le point de temps. Je ne sais pas lequel est le mieux, j'ai comparé les deux ont le même rendement, donc je suppose que c'est une question de préférence. Peut-être Howard pourrait-il intervenir:

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();

Le premier on lit plus clairement dans mon esprit, allant de gauche à droite.

1
répondu Feem 2016-03-31 18:04:21

time_point objets ne prendre en charge l'arithmétique qu'avec d'autres time_point ou duration objets.

Vous aurez besoin de convertir votre longduration d'unités spécifiées, alors votre code devrait fonctionner correctement.

0
répondu Mr. Llama 2015-07-06 21:04:09