ronag
ronag

Reputation: 51255

chrono with different time periods?

Currently I am using boost::rational<std::uint64> to keep track in my application.

Basically I have a clock that runs over a very long period of time and will be tick by different components of different time resolutions, e.g. 1/50s, 1/30s, 1001/30000s etc... I want to maintain perfect precision, i.e. no floating point. boost::rational works well for this purpose, however I think it would be better design to use std::chrono::duration for this.

My problem though is, how can I use std::chrono::duration here? Since it uses a compile time period I don't quite see how I can use it in my scenario where I need to maintain precision?

Upvotes: 1

Views: 1389

Answers (2)

Howard Hinnant
Howard Hinnant

Reputation: 218700

If I'm understanding your question, and if you know all of the different time resolutions at compile-time, then the following will do what you want. You can figure out the correct tick period by using common_type on all of your different time resolutions as shown below:

#include <cstdint>
#include <chrono>

struct clock
{
    typedef std::uint64_t                      rep;
    typedef std::common_type
    <
        std::chrono::duration<rep, std::ratio<1, 50>>,
        std::chrono::duration<rep, std::ratio<1, 30>>,
        std::chrono::duration<rep, std::ratio<1001, 30000>>
    >::type                                    duration;
    typedef duration::period                   period;
    typedef std::chrono::time_point<clock>     time_point;
    static const bool is_steady =              true;

    static time_point now()
    {
        // just as an example
        using namespace std::chrono;
        return time_point(duration_cast<duration>(steady_clock::now().time_since_epoch()));
    }
};

This will compute at compile-time the largest tick period which will exactly represent each of your specified resolutions. For example with this clock one can exactly represent:

  • 1/50 with 600 ticks.
  • 1/30 with 1000 ticks.
  • 1001/30000 with 1001 ticks.

The code below exercises this clock and uses the "chrono_io" facility described here to print out not only the run-time number of ticks of your clock, but also the compile-time units of your clock-tick:

#include <iostream>
#include <thread>
#include "chrono_io"

int main()
{
    auto t0 = clock::now();
    std::this_thread::sleep_for(std::chrono::milliseconds(20));
    auto t1 = clock::now();
    std::cout << (t1-t0) << '\n';
}

For me this prints out:

633 [1/30000]seconds

Meaning: There were 633 clock ticks between calls to now() and the unit of each tick is 1/30000 of a second. If you don't want to be beholden to "chrono_io" you can inspect the units of your clock with clock::period::num and clock::period::den.

If your different time resolutions are not compile-time information, then your current solution with boost::rational is probably best.

Upvotes: 4

Steve Jessop
Steve Jessop

Reputation: 279215

You're allowed to set the period to 1 and use a floating point type for Rep.

I suspect that you can do the same thing with boost::rational, but you'll have to look quite closely at std::chrono, which I haven't done. Look at treat_as_floating_point and duration_values. Also try to figure out what the standard means by "An arithmetic type or a class emulating an arithmetic type".

One might reasonably argue that if boost::rational doesn't emulate an arithmetic type, then it's not doing its job. But it doesn't necessarily follow that it really does everything std::chrono::duration expects.

Upvotes: 1

Related Questions