En C++ 11, vous devez toujours utiliser std::localtime
et std::gmtime
comme indirection pour imprimer un std::chrono::time_point
. Ces fonctions ne sont pas sûres à utiliser dans un environnement multithread comme introduit avec C++ 11 car elles renvoient un pointeur vers une structure statique interne. Ceci est particulièrement ennuyeux puisque C++ 11 a introduit la fonction pratique std::put_time
qui est presque inutilisable pour la même raison.
Pourquoi est-ce si fondamental cassé ou est-ce que j'oublie quelque chose?
Selon N2661 , le papier qui a ajouté <chrono>
:
Ce document n'offre pas de services de calendrier, sauf pour un mappage minimal vers et depuis _
time_t
.Comme cet article ne propose pas de bibliothèque date/heure, ni ne spécifie d'époques, il ne traite pas non plus des secondes intercalaires. Cependant, une bibliothèque de date/heure trouvera que c'est une excellente base sur laquelle construire.
Cet article ne propose pas de bibliothèque de quantités physiques à usage général.
Cet article propose une base solide qui, à l'avenir, pourrait fournir un point de départ compatible pour une bibliothèque générale d'unités physiques. Bien qu'une telle future bibliothèque puisse prendre plusieurs formes, la présente proposition ne fait pas vraiment office de bibliothèque d'unités physiques. Cette proposition est temporelle et continue d'être motivée par les besoins temporels de la bibliothèque de threads.
L'objectif principal de cette proposition est de satisfaire les besoins de l'API de threading de bibliothèque standard d'une manière qui est facile à utiliser, sûre à utiliser, efficace et suffisamment flexible pour ne pas être obsolète dans 10 ou même 100 ans. Chaque fonctionnalité contenue dans cette proposition est ici pour une raison spécifique avec des cas d'utilisation pratiques comme motivation. Les choses qui entraient dans la catégorie "cool", ou "qui sonne comme pouvant être utile", ou "très utile mais pas nécessaire par cette interface" n'ont pas été incluses. Ces éléments peuvent apparaître dans d'autres propositions et éventuellement cibler un TR.
Notez que l'objectif principal de <chrono>
est "pour satisfaire les besoins de l'API de threading de bibliothèque standard", qui ne nécessite pas de services de calendrier.
localtime
et gmtime
ont un stockage interne qui est statique, ce qui signifie qu'ils ne sont pas threadsafe (nous devons renvoyer un pointeur vers une structure de données, il doit donc être alloué dynamiquement, une valeur statique ou une valeur globale - puisque l'allocation dynamique entraînerait une fuite de mémoire, ce n'est pas une solution raisonnable, ce qui signifie qu'il doit s'agir d'une variable globale ou statique [en théorie, on pourrait allouer et stocker dans TLS, et le rendre threadsafe de cette façon]).
La plupart des systèmes ont des alternatives threadsafe, mais ils ne font pas partie de la bibliothèque standard. Par exemple, Linux/Posix a localtime_r
et gmtime_r
, qui prend un paramètre supplémentaire pour le résultat. Voir par exemple http://pubs.opengroup.org/onlinepubs/7908799/xsh/gmtime.html
De même, les bibliothèques Microsoft ont gmtime_s
, qui est également rentrant et fonctionne de manière similaire (en passant le paramètre de sortie en entrée). Voir http://msdn.Microsoft.com/en-us/library/3stkd9be.aspx
Quant à savoir pourquoi la bibliothèque standard C++ 11 n'utilise pas ces fonctions? Que vous devriez demander aux personnes qui ont écrit cette spécification - je m'attends à ce que ce soit la portabilité et la commodité, mais je ne suis pas entièrement sûr.
Il n'y a pas d'alternative threadsafe à std::localtime
et std::gmtime
parce que vous n'en avez pas proposé et que vous avez organisé tout le processus de normalisation. Et personne d'autre non plus.
chrono
seulement le code de calendrier est un code qui enveloppe les time_t
les fonctions. La standardisation ou l'écriture de nouveaux fichiers ne faisait pas partie du domaine du projet chrono
. Faire une telle normalisation nécessiterait plus de temps, plus d'efforts et ajouterait plus de dépendances. Envelopper simplement chaque time_t
la fonction était simple, avait peu de dépendances et rapide.
Ils ont concentré leurs efforts étroitement. Et ils ont réussi ce sur quoi ils se sont concentrés.
Je vous encourage à commencer à travailler sur <calendar>
ou participer à un tel effort pour créer une API de calendrier robuste pour std
. Bonne chance et bonne chance!
Si vous souhaitez utiliser une bibliothèque tierce gratuite et open-source , voici un moyen d'imprimer std::chrono::system_clock::time_point
en UTC:
#include "date.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono;
std::cout << system_clock::now() << " UTC\n";
}
Il s'agit d'une alternative thread-safe à std::gmtime
en utilisant la syntaxe C++ moderne.
Pour un fil moderne et sûr std::localtime
remplacement, vous avez besoin de cette relation étroitement liée bibliothèque de fuseaux horaires de niveau supérieur et la syntaxe ressemble à ceci:
#include "tz.h"
#include <iostream>
int
main()
{
using namespace date;
using namespace std::chrono;
std::cout << make_zoned(current_zone(), system_clock::now()) << "\n";
}
Ces deux éléments sortiront avec la précision de votre system_clock
prend en charge, par exemple:
2016-07-05 10:03:01.608080 EDT
(microsecondes sur macOS)
Ces bibliothèques vont bien au-delà d'un remplacement de gmtime
et localtime
. Par exemple, voulez-vous voir la date actuelle dans le calendrier julien?
#include "julian.h"
#include <iostream>
int
main()
{
using namespace std::chrono;
std::cout << julian::year_month_day(date::floor<date::days>(system_clock::now())) << "\n";
}
2016-06-22
Que diriez-vous de l'heure GPS actuelle?
#include "tz.h"
#include <iostream>
int
main()
{
using namespace date;
std::cout << std::chrono::system_clock::now() << " UTC\n";
std::cout << gps_clock::now() << " GPS\n";
}
2016-07-05 14:13:02.138091 UTC
2016-07-05 14:13:19.138524 GPS
https://github.com/HowardHinnant/date
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0355r0.html
Mise à jour
Les bibliothèques "date.h" et "tz.h" sont maintenant dans le projet de spécification C++ 2a, avec des modifications très mineures, et où nous espérons que "a" est "0". Ils vivront dans l'en-tête <chrono>
et sous namespace std::chrono
(et il n'y aura pas de date namespace
).
Comme d'autres l'avaient mentionné, il n'y a vraiment aucune approche pratique de threadsafe et de formatage de l'heure portable dans aucun standard C++ disponible, mais il y a une technique de préprocesseur archaïque que j'ai trouvée utilisable (merci à Andrei Alexandrescu sur CppCon 2015 slide 17 & 18):
std::mutex gmtime_call_mutex;
template< size_t For_Separating_Instantiations >
std::tm const * utc_impl( std::chrono::system_clock::time_point const & tp )
{
thread_local static std::tm tm = {};
std::time_t const time = std::chrono::system_clock::to_time_t( tp );
{
std::unique_lock< std::mutex > ul( gmtime_call_mutex );
tm = *std::gmtime( &time );
}
return &tm;
}
#ifdef __COUNTER__
#define utc( arg ) utc_impl<__COUNTER__>( (arg) )
#else
#define utc( arg ) utc_impl<__LINE__>( (arg) )
#endif
Ici, nous déclarons la fonction avec size_t
argument de modèle et retour du pointeur au membre statique std::tm
. Maintenant, chaque appel de cette fonction avec un argument de modèle différent crée une nouvelle fonction avec une toute nouvelle statique std::tm
variable. Si __COUNTER__
la macro est définie, elle doit être remplacée par une valeur entière incrémentée à chaque utilisation, sinon nous utilisons __LINE__
macro et dans ce cas, mieux vaut être sûr de ne pas appeler macro utc
deux fois sur une même ligne.
Global gmtime_call_mutex
protéger non threadsafe std::gmtime
appeler à chaque instanciation, et au moins sous Linux ne devrait pas être un problème de performance car l'acquisition de verrou est d'abord effectuée comme tournant autour de spinlock, et dans notre cas ne devrait jamais se retrouver avec un vrai verrou de thread.
thread_local
garantit que différents threads exécutant le même code avec les appels utc
continueront de fonctionner avec différents std::tm
variables.
Exemple d'utilisation:
void work_with_range(
std::chrono::system_clock::time_point from = {}
, std::chrono::system_clock::time_point to = {}
)
{
std::cout << "Will work with range from "
<< ( from == decltype(from)()
? std::put_time( nullptr, "beginning" )
: std::put_time( utc( from ), "%Y-%m-%d %H:%M:%S" )
)
<< " to "
<< ( to == decltype(to)()
? std::put_time( nullptr, "end" )
: std::put_time( utc( to ), "%Y-%m-%d %H:%M:%S" )
)
<< "."
<< std::endl;
// ...
}