Converting between integer std::chrono::durations

user11589013

I'm writing a standard game loop using std::chrono. I need to pass a float to my render method that represents how far into the next frame I am. To calculate the number I'm using the count() method of chrono::duration, hence I need to convert two durations to the same ratio.

void engine::run() {
  using namespace std::chrono;
  using updates = duration<steady_clock::rep, std::ratio<1, 40>>;
  using common = std::common_type<updates, steady_clock::duration>::type;

  constexpr updates time_per_update{1};
  auto previous = steady_clock::now();
  auto lag = steady_clock::duration::zero();

  while (!quit) {
    auto now = steady_clock::now();
    auto delta = now - previous;

    previous = now;
    lag += delta;

    while (lag >= time_per_update) {
      lag -= time_per_update;
      update(time_per_update);
    }

    render(common{lag}.count() / static_cast<double>(common{time_per_update}.count()));
  }
}

If I change the ratio in 'updates' to, say 41, I get a compile error at the subtraction, because a 1/41 of a second cannot be precisely converted to steady_clock::duration. However, when I rewrite the code to this, it compiles just fine:

void engine::run() {
  using namespace std::chrono;
  using updates = duration<steady_clock::rep, std::ratio<1, 41>>;
  using common = std::common_type<updates, steady_clock::duration>::type;

  constexpr common time_per_update{updates{1}};
  auto previous = steady_clock::now();
  common lag = steady_clock::duration::zero();

  while (!quit) {
    auto now = steady_clock::now();
    auto delta = now - previous;

    previous = now;
    lag += delta;

    while (lag >= time_per_update) {
      lag -= time_per_update;
      update(time_per_update);
    }

    render(lag.count() / static_cast<double>(time_per_update.count()));
  }
}

I was under the impression the conversion to common_type happens implicitly during the subtraction. What am I missing? Is there a better way to do this?

Howard Hinnant

To clarify, this is the line of code that gets an error:

 lag -= time_per_update;

test.cpp:27:11: error: no viable overloaded '-='
      lag -= time_per_update;
      ~~~ ^  ~~~~~~~~~~~~~~~

I was under the impression the conversion to common_type happens implicitly during the subtraction.

You are correct, but not exactly correct.

The binary subtraction operator returns the common_type of the two arguments:

template <class Rep1, class Period1, class Rep2, class Period2>
  constexpr
  typename common_type<duration<Rep1, Period1>, duration<Rep2, Period2>>::type
  operator-(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs);

However the -= operator has to be equivalent to:

lag = lag - delta;

And if the result of lag - delta is not implicitly convertible to the type of lag, then you have a problem (as is the case for your example).

Changing the type of lag to common is the correct fix:

common lag = steady_clock::duration::zero();

Here's another way to do that. The choice between these two is stylistic:

auto lag = steady_clock::duration::zero() + updates{0};

Finally, just as personal nit, I like to minimize (if not eliminate) the uses of .count() as this is the equivalent of a reinterpret_cast from duration to integral (or scalar). This is not hard to do in your example, and involves no loss in efficiency.

First add an equivalent to common that uses double as the representation:

using dcommon = duration<double, common::period>;

Then you just convert lag to dcommon and divide that by time_per_update to get your ratio:

render(dcommon{lag} / time_per_update);

Finally, there is one more stylistic variation for you to consider: Just represent lag as double-based up front:

auto lag = steady_clock::duration::zero() + updates{0} + duration<double>{0};

Now your call to render is even simpler:

render(lag / time_per_update);

And common and dcommon are no longer even needed (you can remove them).

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

How to format std::chrono durations?

dividing and multiplying std::chrono::durations

Converting between boost units and durations

What is the best way to form custom std::chrono::durations and std::ratios?

Differentiating std::chrono durations that have the same type and ratio?

Determine the part of chrono durations

Converting from struct timespec to std::chrono::?

Converting chrono::duration type to std::tm type

Converting trip durations to parking durations

Converting Java to Scala durations

Why won't smaller chrono::durations subtract?

Dividing two chrono::durations to get fraction

Converting Between std::bitset and std::vector<bool>

Difference between durations

C++ std::tm returns wrong value after converting it from std::chrono::time_point

Casting time_points between std::chrono clocks

unexpected bug in conversion between std::chrono::duration with different Rep

operator= between two std::chrono::time_point cause error

Cannot convert between std::chrono::time_point s

how many days left between 2 dates using std::chrono

Converting boost::chrono::steady_clock::time_point to std::chrono::steady_clock::time_point

Using std::chrono / date::gps_clock for converting a double gps timestamp to utc/tai

std::chrono::time_point compiler error when converting from a variable

Comparing chrono::duration with an integer

Use of variadic argument ellipsis in a type trait for chrono durations

std::chrono for frequencies?

How accurate is std::chrono?

Subclassing std::chrono::duration

Leap seconds and std::chrono