Как уже упоминалось, на самом деле ни в одном доступном стандарте C++ нет многопоточного удобства и переносимого подхода к форматированию времени, но есть некоторая архаичная техника препроцессора, которую я нашел полезной (спасибо Андрею Александреску на CppCon 2015 слайд 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
Здесь мы объявляем функцию с аргументом шаблона size_t
и возвращаем указатель на статический член std::tm
. Теперь каждый вызов этой функции с другим аргументом шаблона создает новую функцию с совершенно новой статической переменной std::tm
. Если определен макрос __COUNTER__
, он должен заменяться увеличивающимся целочисленным значением каждый раз, когда он используется, в противном случае мы используем макрос __LINE__
и в этом случае лучше быть уверенным, что мы не вызываем макрос utc
дважды в одной строке.
Глобальный gmtime_call_mutex
защищает вызов std::gmtime
без многопоточности в каждом экземпляре, и, по крайней мере, в Linux не должно быть проблем с производительностью, поскольку получение блокировки сначала выполняется как обход спин-блокировки, и в нашем случае никогда не должно заканчиваться реальной блокировкой потока.
thread_local
гарантирует, что разные потоки, выполняющие один и тот же код с вызовами utc
, будут по-прежнему работать с разными переменными std::tm
.
Пример использования:
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;
// ...
}
person
Felix Vanorder
schedule
20.03.2016
strtok
. - person CodesInChaos   schedule 02.09.2014