localtime_r 的 C++11 替代方案

Posted

技术标签:

【中文标题】localtime_r 的 C++11 替代方案【英文标题】:C++11 alternative to localtime_r 【发布时间】:2011-11-10 22:49:55 【问题描述】:

C++ 以strftime 定义时间格式化函数,这需要struct tm“分解时间”记录。但是,C 和 C++03 语言没有提供线程安全的方式来获取这样的记录;整个程序只有一位大师struct tm

在 C++03 中,这或多或少是可以的,因为该语言不支持多线程;它仅支持支持多线程的平台,然后提供诸如 POSIX localtime_r 之类的设施。

C++11 还定义了新的时间实用程序,它与非分解的time_t 类型接口,用于重新初始化全局struct tm 的类型。但是获得time_t 不是问题。

我是否遗漏了什么或者这项任务仍然需要依赖 POSIX?

编辑:这是一些解决方法代码。它保持与提供::localtime_r 的多线程环境和仅提供std::localtime 的单线程环境的兼容性。它也可以很容易地适应检查其他功能,例如posix::localtime_r::localtime_s 或what-have-you。

namespace query 
    char localtime_r( ... );

    struct has_localtime_r
         enum  value = sizeof localtime_r( std::declval< std::time_t * >(), std::declval< std::tm * >() )
                        == sizeof( std::tm * ) ; ;


    template< bool available > struct safest_localtime 
        static std::tm *call( std::time_t const *t, std::tm *r )
             return localtime_r( t, r ); 
    ;

    template<> struct safest_localtime< false > 
        static std::tm *call( std::time_t const *t, std::tm *r )
             return std::localtime( t ); 
    ;

std::tm *localtime( std::time_t const *t, std::tm *r )
     return query::safest_localtime< query::has_localtime_r::value >().call( t, r ); 

【问题讨论】:

你不能把localtime 包装在一个使用互斥锁的函数中吗?还是其他重量更轻的锁?我看不到这里需要 POSIX。 @Nicol:是的,但这只有在没有其他人尝试做同样的事情时才是安全的。换句话说,它在程序中工作,而不是在库中。 但是他们会从线程代码中调用一个非线程安全的函数,所以他们会得到他们应得的。您可以只公开一个函数供他们使用,如果他们使用标准 C 函数,那么他们会得到未定义的行为。 @Nicol:什么?我正在编写一个无法向其用户提供此类限制的库。 @Nicol:我的库不是多线程的,但我希望它与多线程代码兼容。您建议使用互斥锁并致电localtime 正是您建议我告诉用户不要这样做的建议。我不能坚持认为我的互斥锁是localtimethe 互斥锁——库的功能是不相关的。无论如何,与互斥锁有关的任何事情都是错误的解决方案,因为我的代码绝不是多线程的,它只是线程安全的。 【参考方案1】:

你没有错过任何东西。

下一个 C 标准(可能在今年发布)确实在附录 K 中定义:

struct tm *localtime_s(const time_t * restrict timer,
                       struct tm * restrict result);

而且这个新函数是线程安全的!但不要太高兴。有两个主要问题:

    localtime_s 是 C11 的可选扩展。

    C++11 引用 C99,而不是 C11。 local_time_s 在 C++11 中找不到,可选与否。

更新

自从我回答这个问题以来的 4 年里,我也对这个领域的 C++ 工具的糟糕设计感到沮丧。我有动力创建现代 C++ 工具来处理这个问题:

http://howardhinnant.github.io/date/tz.html

#include "tz.h"
#include <iostream>

int
main()

    using namespace date;
    auto local_time = make_zoned(current_zone(), std::chrono::system_clock::now());
    std::cout << local_time << '\n';

这只是为我输出:

2015-10-28 14:17:31.980135 美国东部时间

local_timestd::chrono::system_clock::time_pointtime_zone 的配对,表示当地时间。

存在用于将std::chrono::system_clock::time_point 分解为人类可读字段类型的实用程序,例如年、月、日、小时、分钟、秒和亚秒。这是一个专注于那些(非时区)部分的演示文稿:

https://www.youtube.com/watch?v=tzyGjOm8AKo

所有这些当然是线程安全的(它是现代 C++)。

更新 2

以上内容现在是 C++20 的一部分,语法略有改变:

#include <chrono>
#include <iostream>

int
main()

    namespace chr = std::chrono;

    chr::zoned_time local_timechr::current_zone(), chr::system_clock::now();
    std::cout << local_time << '\n';

【讨论】:

... 他们改名了!谢谢(你的)信息。无论如何,有可能查询localtime_r 是否可用,否则返回localtime。所以看起来我可以吃蛋糕,毕竟。

以上是关于localtime_r 的 C++11 替代方案的主要内容,如果未能解决你的问题,请参考以下文章

C# WPF OnPaint 方法的替代方案?

如何提高子查询的性能或 sql 中子查询的替代方案

localtime() 与 localtime_r() 中时区变化的实时感知

centos7(redhat7) 中localtime_r有一个BUG

localtime 和 localtime_r

11 岁以下 api 的 setTop 替代方案