boostorg / chrono

Boost.org chrono module
http://boost.org/libs/chrono
15 stars 52 forks source link

std::chrono interoperability #41

Open Mike-Devel opened 5 years ago

Mike-Devel commented 5 years ago

As currently discussed on the mailing list, boost::chrono has a poor interoperability story with the std::chrono types. As a short term improvement, I'd like to add to_std_duration, to_std_time_point and to_boost_duration, to_boost_time_point functions to this library.

Any comments on this idea before I start working on a PR?

viboes commented 5 years ago

I've not followed thé discussion. Could you tell me what os the use case?

Mike-Devel commented 5 years ago

Apparently some users complained (this was relayed by Howard Hinnand IIRC), that there is not easy interoperability between functions that use std::chrono and boost::chrono types.

If I'm not mistaken, the following is not possible:

std::chrono::milliseconds foo();
void bar(boost::chrono::milliseconds);

bar(foo());

and it becomes worse, when functions are templated and/or have mismatching periods (e.g. milli vs micro).

I don't want to repeat the whole discussion, but long-term, my favorite solution (proposed by Alex Grund) would be to make the boost types and functions aliases for the c++11 chrono types as far as possible and only implement the extensions (such as I/O) in Boost.Chrono.

Of course that may be a complete pipe-dream (no idea, what your Opinion on that matter is) and there will certainly be a lot of discussions and a deprecation period before that can happen anyway. Hence I think, it might be a nice short-term fix, to at least provide appropriate explicit conversion functions.

viboes commented 5 years ago

Ok, l'll consider a complete pr with tests and documentation

ccorn commented 4 years ago

To encourage deliberations about the finer points, let me present code that I have been using so far. Abbreviations:

    namespace sc = ::std::chrono;
    namespace bc = ::boost::chrono;

Part 1: Type conversion between std::ratio and boost::ratio. This should supplement Boost.Ratio. The code below accepts anything with static num and den members and thus maps non-normalized ratios to normalized ratios, which may be counterintuitive, but this way I do not need to specify more detailed patterns with specific non-type template arguments. You may want to use a stricter approach instead.

    // std::is_same_v<to_std_ratio_t<boost::ratio<N,D>>, std::ratio<N,D>>
    // if and only if N, D are coprime and D is positive
    template<typename Ratio>
    using to_std_ratio_t = std::ratio<Ratio::num, Ratio::den>;

    // std::is_same_v<to_boost_ratio_t<std::ratio<N,D>>, boost::ratio<N,D>>
    // if and only if N, D are coprime and D is positive
    template<typename Ratio>
    using to_boost_ratio_t = boost::ratio<Ratio::num, Ratio::den>;

Part 2: Conversion functions between {std,boost}::{duration,time_point}:

    template<typename Rep, typename Period> inline constexpr
    sc::duration<Rep, to_std_ratio_t<Period>>
    to_std_chrono(const bc::duration<Rep, Period> &d)
    {
        return sc::duration<Rep, to_std_ratio_t<Period>>
                (d.count());
    }

    template<typename Rep, typename Period> inline constexpr
    sc::time_point<sc::system_clock,
                   sc::duration<Rep, to_std_ratio_t<Period>>>
    to_std_chrono(const bc::time_point<bc::system_clock,
                                       bc::duration<Rep, Period>> &t)
    {
        // Assuming same epoch
        return sc::time_point<sc::system_clock,
                              sc::duration<Rep, to_std_ratio_t<Period>>>
                (to_std_chrono(t.time_since_epoch()));
    }

    template<typename Rep, typename Period> inline constexpr
    bc::duration<Rep, to_boost_ratio_t<Period>>
    to_boost_chrono(const sc::duration<Rep, Period> &d)
    {
        return bc::duration<Rep, to_boost_ratio_t<Period>>
                (d.count());
    }

    template<typename Rep, typename Period> inline constexpr
    bc::time_point<bc::system_clock,
                   bc::duration<Rep, to_boost_ratio_t<Period>>>
    to_boost_chrono(const sc::time_point<sc::system_clock,
                                         sc::duration<Rep, Period>> &t)
    {
        // Assuming same epoch
        return bc::time_point<bc::system_clock,
                              bc::duration<Rep, to_boost_ratio_t<Period>>>
                (to_boost_chrono(t.time_since_epoch()));
    }

The names are just to_std_chrono and to_boost_chrono. More overloads would cover clocks other than system_clock, e.g. steady_clock. The time_point conversion assumes that the boost and std versions of the specified clock are functionally the same, and that their epochs are the same. Things get tricky if you cannot make such assumptions.

Mike-Devel commented 4 years ago

Thanks for reminding me about this. I actually had a working version of this for quite some time now, but never found the time/motivation to add the docs and write more tests.

With the recent concern about anonymous contributors on the boost mailing list, I thought it was better if I ceased further work on boost altogether, but if that is not a problem for you, feel free to use that code: I just committed my changes to https://github.com/Mike-Devel/chrono/tree/std_interop. Here is the diff: https://github.com/Mike-Devel/chrono/commit/9d09efb7520d974f7db86dc24b8b9ed6146ca0c0 )

d3matt commented 2 weeks ago

FWIW: as of boost 1.84, boost::ratio is a type alias of std::ratio. So Point 1 above should be done...