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?
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.
Comments