web-dev-qa-db-fra.com

C ++ Comment convertir un std :: chrono :: time_point en long and back

J'ai besoin de convertir std::chrono::time_point vers et depuis un type long (entier 64 bits). Je commence à travailler 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 est compilé, mais ne montre pas le succès.

Pourquoi dt est-il différent de now à la fin?

Que manque-t-il sur ce code?

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

C'est un super endroit pour auto:

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

Puisque vous voulez faire du trafic avec une précision de millisecond, il serait bon d’aller de l’avant et de le dissimuler dans le 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 de milliseconds à la place de la précision de votre system_clock.

auto Epoch = now_ms.time_since_Epoch();

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

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

Ici:

long duration = value.count();

Dans your et my code, duration contient le nombre de milliseconds depuis l'époque de system_clock.

Cette:

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

Crée un duration représenté par un long et une précision de seconds. Ceci efficacement reinterpret_cast Est le milliseconds détenu dans value à seconds. C'est une erreur de logique. Le code correct ressemblerait à ceci:

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

Cette ligne:

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

crée un time_point basé sur system_clock, avec la possibilité de conserver une précision proche de la précision native de system_clock (généralement plus fine que des millisecondes). Cependant, la valeur d'exécution indiquera correctement qu'un nombre entier de millisecondes est conservé (en supposant que je corrige le type de dur).

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

if (dt != now)

Parce que dt contient un nombre entier de milliseconds, mais now contient un nombre entier de ticks plus fins qu'un millisecond (par exemple, microseconds ou nanoseconds). Ainsi, le test ne réussira que très rarement lorsque system_clock::now() retournera un nombre entier de milliseconds.

Mais vous pouvez plutôt:

if (dt != now_ms)

Et vous allez maintenant obtenir votre résultat attendu de manière 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 je les coderais ainsi:

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

Ce qui produira de manière fiable:

Success.

Enfin, je recommande d'éliminer les temporaires pour réduire la conversion de code entre time_point Et le type intégral au minimum. Ces conversions sont dangereuses et moins vous écrirez de code en manipulant le type intégral nu, mieux ce sera:

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 pas interpréter integral_duration Comme milliseconds sur le chemin du retour à un time_point. Un moyen possible d'atténuer ce risque est d'écrire:

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

Cela réduit les risques au point de s'assurer que vous utilisez sys_milliseconds À l'aller et aux deux endroits à votre retour.

Et encore un exemple: supposons que vous souhaitiez convertir en une intégrale représentant la durée que system_clock Prend en charge (microsecondes, 10th microsecondes ou nanosecondes). Ensuite, vous n'avez pas à vous soucier de spécifier les millisecondes comme ci-dessus. Le code se simplifie pour:

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 (sur l'intégrale) sur une plate-forme et l'autre moitié (sur celle de l'intégrale) sur une autre plate-forme, vous courez le risque que system_clock::duration Ait des précisions différentes pour les deux conversions. .

128
Howard Hinnant

Je voudrais également noter qu'il y a deux façons d'obtenir le nombre de ms dans le point temporel. Je ne sais pas lequel est le meilleur, je les ai comparés et ils ont tous les deux la même performance, alors je suppose que c'est une question de préférence. Peut-être qu'Howard pourrait 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 lit plus clairement dans mon esprit en allant de gauche à droite.

2
Feem

time_point objets ne supporte l'arithmétique qu'avec d'autres time_point ou duration objets.

Vous devrez convertir votre long en un duration d'unités spécifiées. Votre code devrait alors fonctionner correctement.

0
Mr. Llama

comme une seule ligne:

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
Harald P.