【问题标题】:Implicit conversion between c++11 clocks/time_pointsc++11时钟/time_points之间的隐式转换
【发布时间】:2017-12-24 17:43:50
【问题描述】:

是否可以在两个 C++11 时钟的time_points 之间进行隐式/显式转换?

动机chrono::durations 提供了存储从纪元开始的时间间隔的方法,从概念上讲,它不等于具有自己的纪元的自定义时钟的 time_point。 在时钟之间进行隐式转换可以简化 Howard Hinnant 的date library<date/date.h> 的使用,它提供了操作和打印出time_points 的系统时钟的方法。

示例:

#include <date/date.h>
using namespace date;
namespace ch = std::chrono;
// 
#define EPOCH_OFFSET 100
template<class Duration> using PosixTimePoint = 
                   ch::time_point<ch::system_clock, Duration>;
typedef PosixTimePoint<ch::duration<long,std::micro>> PosixTimePointType;

struct SomeClock{
    typedef ch::duration<long,std::micro>   duration;
    typedef ch::time_point<SomeClock>  time_point;

    ...
    static time_point now() noexcept {
        using namespace std::chrono;
        return time_point (
            duration_cast<duration>( 
                system_clock::now().time_since_epoch()) + date::years(EPOCH_OFFSET) );
    }
    static PosixTimePoint<duration> to_posix( const time_point& tp  ){...}

}
auto tp = SomeClock::now(); //<time_point<SomeClock,ch::duration<long,std::micro>>;

目标: 转换 tp 以便 std::stream 转换 date.h 有效并打印出当前时间,在我的情况下是:2017-12-24 17 :02:56.000000

// std::cout << tp; compile error
std::cout << SomeClock::to_posix( tp ); // OK

显式转换:这可以提高可读性,支持语言的转换功能并方便访问date.h 例程。

long time_value = static_cast<long>( tp );
auto st = static_cast<PosixTimePointType>( tp ); 
std::cout << static_cast<PosixTimePointType>( tp );

【问题讨论】:

    标签: date c++11 time chrono


    【解决方案1】:

    我建议模仿tz.h 中的date::utc_clockdate::tai_clock 的实现。例如utc_clock 实现了两个函数来与sys_time 相互转换:

    template<typename Duration>
    static
    std::chrono::time_point<std::chrono::system_clock, typename std::common_type<Duration, std::chrono::seconds>::type>
    to_sys(const std::chrono::time_point<utc_clock, Duration>&);
    
    template<typename Duration>
    static
    std::chrono::time_point<utc_clock, typename std::common_type<Duration, std::chrono::seconds>::type>
    from_sys(const std::chrono::time_point<std::chrono::system_clock, Duration>&);
    

    因此您可以将std::chrono::system_clock 视为“中心”。任何实现这些转换的时钟都可以通过在封面下的system_clock 反弹来转换为实现这些转换的任何其他时钟。为了促进这种反弹,引入了date::clock_cast

    此外,utc_time 可以用作集线器,如果这对您的类型更有效的话。例如tai_clock 实现:

    template<typename Duration>
    static
    std::chrono::time_point<utc_clock, typename std::common_type<Duration, std::chrono::seconds>::type>
    to_utc(const std::chrono::time_point<tai_clock, Duration>&) NOEXCEPT;
    
    template<typename Duration>
    static
    std::chrono::time_point<tai_clock, typename std::common_type<Duration, std::chrono::seconds>::type>
    from_utc(const std::chrono::time_point<utc_clock, Duration>&) NOEXCEPT;
    

    clock_cast 足够聪明,可以处理这种“双中心”系统,因此可以将一个与utc_time 相互转换的时钟转换为另一个使用sys_time 作为其中心的时钟。

    如果你还为你的时钟实现了to_stream,那么你可以直接使用format来格式化你的clock::time_point。而clock_cast 很可能在您的to_stream 函数的实现中很有用。

    还可以使用from_stream 将您的clock::time_pointdate::parse 挂钩。

    https://howardhinnant.github.io/date/tz.html 中搜索“clock_cast”,例如它的用法。对于您的用例,to_sys/from_sys API 似乎是最有用的。只需这两个函数,您就可以在 SomeClock 和 tz.h 中的任何其他时钟(以及满足这些要求的任何其他自定义时钟)之间使用 clock_cast


    完整演示

    #include "date/tz.h"
    #include <iostream>
    #include <sstream>
    
    struct SomeClock
    {
        using duration = std::chrono::microseconds;
        using rep = duration::rep;
        using period = duration::period;
        using time_point = std::chrono::time_point<SomeClock>;
        static constexpr bool is_steady = false;
    
        static time_point now() noexcept
        {
            return from_sys(date::floor<duration>(std::chrono::system_clock::now()));
        }
    
        static constexpr auto offset = date::sys_days{} - date::sys_days{date::year{1870}/1/1};
    
        template<typename Duration>
        static
        date::sys_time<Duration>
        to_sys(const std::chrono::time_point<SomeClock, Duration>& t)
        {
            return date::sys_time<Duration>{(t - offset).time_since_epoch()};
        }
    
        template<typename Duration>
        static
        std::chrono::time_point<SomeClock, Duration>
        from_sys(const date::sys_time<Duration>& t)
        {
            return std::chrono::time_point<SomeClock, Duration>{(t + offset).time_since_epoch()};
        }
    };
    
    template <class Duration>
    using SomeTime = std::chrono::time_point<SomeClock, Duration>;
    
    constexpr date::days SomeClock::offset;
    
    template <class CharT, class Traits, class Duration>
    std::basic_ostream<CharT, Traits>&
    to_stream(std::basic_ostream<CharT, Traits>& os, const CharT* fmt,
              const SomeTime<Duration>& t)
    {
        return date::to_stream(os, fmt, date::clock_cast<std::chrono::system_clock>(t));
    }
    
    template <class CharT, class Traits, class Duration>
    std::basic_ostream<CharT, Traits>&
    operator<<(std::basic_ostream<CharT, Traits>& os, const SomeTime<Duration>& t)
    {
        const CharT fmt[] = {'%', 'F', ' ', '%', 'T', CharT{}};
        return to_stream(os, fmt, t);
    }
    
    template <class Duration, class CharT, class Traits, class Alloc = std::allocator<CharT>>
    std::basic_istream<CharT, Traits>&
    from_stream(std::basic_istream<CharT, Traits>& is, const CharT* fmt,
                SomeTime<Duration>& tp, std::basic_string<CharT, Traits, Alloc>* abbrev = nullptr,
                std::chrono::minutes* offset = nullptr)
    {
        using namespace date;
        sys_time<Duration> st;
        date::from_stream(is, fmt, st, abbrev, offset);
        if (!is.fail())
            tp = clock_cast<SomeClock>(st);
        return is;
    }
    
    int
    main()
    {
        std::cout << SomeClock::now() << '\n';
        std::cout << date::format("%a, %b %d, %Y\n", SomeClock::now());
        std::istringstream in{"2017-12-24 19:52:30"};
        SomeClock::time_point t;
        in >> date::parse("%F %T", t);
        std::cout << t << '\n';
    }
    

    【讨论】:

    • 精美的圣诞礼物,谢谢!由于某些原因,当使用 float/double 底层表示时,流在 sys_time 中失败。我是否犯了一个错误,或者我应该将持续时间转换为整数代表类型,然后按照您概述的“从 sys_time 反弹”方法进行时钟转换?
    • 对于格式化,转换为整数表示类型,精度指示您希望在几秒钟内出现的小数位数。例如,如果您想要 3 个小数位表示秒,floor&lt;milliseconds&gt;(t)
    猜你喜欢
    • 2016-05-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多