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