chrono and sge timers

Abstract

This (relatively short) article will explain to you the concepts of chrono, the “time library” of the latest C++ standard. It will also explain how timers in sge work.

Motivation

chrono is defined in the latest standard, but until you decide to use C++0x instead of the good olde C++03, you need to use an alternative implementation (luckily, implementing chrono doesn’t require other C++0x features – it stands on its own).

There are two implementations of chrono: fcppt::chrono and boost::chrono. The latter, however, requires boost-1.47.0, so we’re going to use fcppt::chrono. Both are virtually identical, anyway, since they both implement the same standard.

We want to “manage time”. This means that we don’t care about the current day of month or how many days there are until christmas. Things are simpler and more abstract in chrono. We’ve got three “concepts” to learn:

  1. Clocks
  2. Time points
  3. Durations

Let’s start with clocks. A clock is something that you can ask for the current time. But how do you define “current time” in abstract terms? Surely we won’t get back a string “07/30/2011 14:58:58”, since we want to do calculations with the return type.

So instead, you get a number which tells you how much “time units” have expired since the clock’s “epoch”. If you’ve heard about the Unix Timestamp, you’ll immediately feel at home. This particular timestamp measures the seconds (or sometimes milliseconds/microseconds, depending on the book you read) since 01/01/1970 0:00. That’s a number you can do calculations with! For example, taking two Unix Timestamps and subtracting them gives you the difference in seconds between two time points. Using that, we could define:

typedef
long
time_point;

typedef
long
duration;

class unix_clock
{
public:
  time_point now() const
  {
    return current_unix_timestamp();
  }
};

This is, of course, very rudimentary. It would be better if time_point and duration were classes with overloaded operators so we can write things like:

unix_clock clock;
time_point current = clock.now();
duration difference = clock.now() - current;

And it would print out how much time has passed between the subtraction and the assignment (probably “0” for most clocks).

Compile time fractions, wtf?

Apart from the Unix Timestamp, there are other clocks imaginable. For example, a clock could define the “epoch” as the duration in seconds since the system start, measuring these seconds as float. Or we could have a clock having the epoch at 0 a.d., measuring in days. And so on.

All of these clocks have different native durations (seconds in integers, seconds in float, days in integers, …). We cannot, however, convert between these different durations yet, since we have no specification on how they relate to each other. They’re just numbers as of yet.

That’s why in chrono, a duration has another piece of information included in its type: A fraction “1/m” telling you how the duration relates to “1 second”. For example, a duration

chrono::duration
<
  int,
  ratio::object<1,1000>
>

tells you that the integer stored in it represents “milliseconds”. As you can see, these fractions are compile time properties, just as the duration of a clock is a compile time property.

Getting more concrete

Clock API

Let’s look at the definition of a clock in fcppt::chrono:

class clock
{
public:
  typedef implementation_defined rep;

  // m and n are implementation defined
  typedef ratio::object<m,n> period;

  typedef 
  chrono::duration
  <
    rep,
    period
  > 
  duration;

  typedef 
  chrono::time_point
  <
    clock
  > 
  time_point;

  static bool const is_steady = false;

  static time_point
  now();
};

Let’s go through it one by one.

The first typedef, rep, is the numeric value of the clock (that’s the type calculations are done with). This is usually something pretty large, like a 64 bit integer, so the clock doesn’t “wrap around” too quickly. But as with durations, this can be a floating point type, too!

The second typedef, period, is the fraction I talked about in the previous section.

Then we have durations and time points. Time points just know about the clock type.

The boolean is_steady tells you if the clock guarantees “steadiness”. This means that for two time points t1 and t2, returned by the now function, t1 <= t2 must hold. Additionally, the time between clock ticks must be constant.

Duration API

The duration type looks like this:

template
<
  typename Rep,
  typename Period
>
class duration
{
public:
  typedef Rep rep;
  typedef Period period;

  /// Default constructs a duration with an undeterminate value
  duration();

  /// Constructs a duration from a compatible internal representation
  /**
   * For example seconds(10) will result in a duration representing 10 seconds
  */
  template<typename Rep2>
  explicit duration(
    Rep2 const &amp;);

  /// Constructs a duration from another compatible duration and converts if necessary.
  template<typename Rep2,typename Period2>
  duration(
    duration<Rep2,Period2> const &amp;);

  /// Returns the internal representation
  rep
  count() const;

  duration
  operator+() const;

  duration
  operator-() const;

  duration &amp;
  operator++();

  duration
  operator++(int);

  duration &amp;
  operator--();

  duration
  operator--(int);

  duration &amp;
  operator+=(
    duration const &amp;);

  duration &amp;
  operator-=(
    duration const &amp;);

  duration &amp;
  operator*=(
    rep const &amp;);

  duration &amp;
  operator/=(
    rep const &amp;);

  duration &amp;
  operator%=(
    rep const &amp;);

  duration &amp;
  operator%=(
    duration const &amp;);

  /// The duration with a zero value
  static duration
  zero();

  /// The duration with the minimum value
  static duration
  min();

  /// The duration with the maximum value
  static duration
  max();
private:
  rep rep_;
};

As you can see, you can get the numeric value from the duration and do calculations with durations like subtracting them from another. These calculations, however, can only be performed with durations of the same type. Stuff like…

fcppt::chrono::seconds myseconds(1);
myseconds -= fcppt::chrono::milliseconds(10);

is not possible. However, there are binary variants of these operators so you can subtract/add/… two arbitrary durations:

result_duration_type result = 
  fcppt::chrono::seconds(1) - fcppt::chrono::milliseconds(10);

But what’s result_duration_type, you ask? Well, in general, that’s not so simple. The result will be a duration with at least the resolution milliseconds, so no information is lost. If you want to convert the result to a coarser duration, you have to use duration_cast. So, put simply, something sane will be produced and you cannot shoot yourself in the foot that easily.

Time point API

template
<
  typename Clock,
  typename Duration
>   
class time_point
{
public:
  typedef Clock clock;
  typedef Duration duration;
  typedef typename duration::rep rep;
  typedef typename duration::period period;
  
  time_point();
  
  /// Constructs a time_point from a duration.
  /**
   * This duration is interpreted as if it were obtained from time_since_epoch().
  */
  explicit time_point(
    duration const &amp;);
  
  /// Constructs a time_point from a compatible time_point.
  /**
   * This may convert if necessary.
  */
  template<typename Duration2>
  time_point(
    time_point<clock, Duration2> const &amp;);
  
  /// Returns the duration from the beginning of the Clock to this time_point
  duration
  time_since_epoch() const;
  
  time_point &amp;
  operator +=(
    duration const &amp;);

  time_point &amp;
  operator -=(
    duration const &amp;);

  /// The minimal time_point
  static time_point
  min();

  /// The maximal time_point
  static time_point
  max();
private:
  duration d_;
};

As you can see, a time point is just a wrapper around a duration, really. But I wanted to show you its api, anyway. As with durations, there are binary operators for subtracting time points or adding a duration to a time point which result in a duration (in the clock’s duration type) and a new time point, respectively.

Example: Limiting your game’s frame rate

To show the expressivity of chrono, I’m going to show you how to limit your game’s frame rate so that it doesn’t exceed a certain number of frames per second (yes, we should be using vertical sync and a blocking swap to do that. Let’s pretend those mechanisms don’t work).

We have to decide on a clock to use. chrono provides a few predefined clocks, among them high_resolution_clock. This clock might not be steady (see above), but since we’re dealing with small time deltas here, a high-resolution clock seems like a wise choice (its internal duration ratio is nanoseconds).

Let me show you the code and figure out for yourselves how it works (this is an exercise, not laziness on my side ;))

#include <fcppt/chrono/high_resolution_clock.hpp>
#include <fcppt/chrono/duration_cast.hpp>
#include <fcppt/chrono/duration.hpp>
#include <fcppt/chrono/duration_arithmetic.hpp>
#include <fcppt/chrono/time_point.hpp>
#include <fcppt/chrono/time_point_arithmetic.hpp>
#include <fcppt/chrono/seconds.hpp>
#include <fcppt/time/sleep_duration.hpp>
#include <fcppt/time/sleep.hpp>

typedef fcppt::chrono::high_resolution_clock clock_type;

typedef clock_type::rep fps_type;

fps_type desired_frames_per_second = 
	60;

// Divide by a rep, get a new duration
clock_type::duration minimum_frame_length(
	fcppt::chrono::duration_cast<clock_type::duration>(
		fcppt::chrono::seconds(1)) / desired_frames_per_second);
	
clock_type::time_point const before_frame = 
	clock_type::now();

render_stuff();

// Subtract two time points, get a clock duration!
clock_type::duration const diff = 
	clock_type::now() - before_frame;

// If the frame was over too quickly, compensate
if(diff < minimum_frame_length)
{
	// Subtract two durations
	fcppt::time::sleep(
		fcppt::chrono::duration_cast<fcppt::time::sleep_duration>(
			minimum_frame_length - diff));
}

sge::timer

In your application (your game, for example), you may want to time things. Say you have a bonus system in your game and you want to let the bonus expire after 10 seconds while the player sees the seconds running down. You can do that with chrono, the structures are there…but it’s not very comfortable. You’d have to store a time point bonus_started yourself, along with a duration bonus_duration and watch if the timer has expired.

In sge, you can define a sge::timer::basic<Clock> which gets a clock as template parameter. Again, in your application, you have to decide which type of clock you want to use. A timer has the following properties:

  1. An interval determining how long it lasts. Internally, this is stored in the clock’s duration type. The interface, however, doesn’t care about the specific type of the duration. You can pass in and retrieve it as any duration type and it will be implicitly converted.
  2. A starting time point, pretty self-explanatory. The only way this member can be modified is to call reset() on the timer.
  3. An activation state which is simply a bool.
  4. An expiration flag which determines if the timer has expired. This can be set explicitly, but is otherwise determined by looking at the interval, the starting time point and the activation state.

Timers can be active or inactive, and they can be expired or not expired. This might be a bit confusing, so let me clarify: A timer is expired, if…

  1. you’ve explicitly set it to be expired (via timer.expired(true))
  2. it’s inactive
  3. the specified interval has elapsed since the last call to reset() or since the timer’s construction

A timer is active if you’ve set it to be active. It’s inactive if you say timer.active(false);. Note that the active flag will not be set to true when you call reset(). The expired flag, however, will be set to false.

The timer interface looks like this

template<typename Clock>
class basic
{
FCPPT_NONCOPYABLE(
	basic);
public:
	typedef
	Clock
	clock_type;

	typedef
	timer::parameters<clock_type>
	parameters;

	typedef typename
	clock_type::time_point
	time_point;

	typedef typename
	clock_type::duration
	duration;

	explicit basic(
		parameters const &amp;);

	bool expired() const;
	void expired(bool);

	bool active() const;
	void active(bool); 

	template<typename NewDuration>
	NewDuration const interval() const;

	template<typename NewDuration>
	void interval(NewDuration const &amp;);

	time_point const now() const;

	time_point const last_time() const;

	void reset();
private:
	duration interval_;
	bool active_;
	bool expired_;
	time_point last_time_;
};

Since we don’t want to specify all of the 4 properties of a timer when initializing it, there is a parameters class which you give to the constructor. Again, I think the timer is best explained with a simple example: Let’s stall the user for, say, 10 seconds and output a nice progress bar while they are waiting:

#include <sge/timer/basic.hpp>
#include <sge/timer/elapsed_fractional.hpp>
#include <fcppt/chrono/seconds.hpp>
#include <fcppt/chrono/high_resolution_clock.hpp>
#include <iostream>

typedef
sge::timer::basic<fcppt::chrono::high_resolution_clock>
timer_type;

timer_type wait_timer(
	timer_type::parameters(
		fcppt::chrono::seconds(10))
		// Unnecessary, just here for exposition
		.active(true)
		.expired(false));

// Let's reserve 60 characters for the progress bar.
unsigned const progress_bar_width = 60;

while(!wait_timer.expired())
{
	// Retrieve the elapsed time as a floating point value in [0,1]
	double const fraction = 
		sge::timer::elapsed_fractional<double>(wait_timer_);

	unsigned const elapsed_time = 
		static_cast<unsigned>(fraction * progress_bar_width);
	
	// \r to rewind to the start of the line
	std::cout << '\r' << '|';
	// Draw the elapsed_time. We could also use one of std::string's constructors
	// for this.
	for(unsigned i = 0; i < elapsed_time; ++i)
		std::cout << '-';
	// Draw the remaining time
	for(unsigned i = 0; i < (progress_bar_width - elapsed_time); ++i)
		std::cout << ' ';
	std::cout << '|';
}

More clocks

There’s one more thing sge::timer provides, and that’s additional clocks. chrono defines three clocks:

  1. high_resolution_clock we already saw
  2. steady_clock which is guaranteed to be steady
  3. system_clock which provides functions to interact with C’s time api (convert to and from time_t values)

All of these clocks are stateless. They are classes, but they contain no data member. That’s why they have a static member function now(). Now, unfortunately, they can be instantiated:

fcppt::chrono::high_resolution_clock my_highres_clock;
fcppt::chrono::high_resolution_clock::time_point my_now = my_highres_clock.now();

but this is just a bad design decision in the Standard.

Despite that, stateful clocks can be useful, too. For example, sge provides sge::timer::clocks::adjustable<Clock>, which takes a stateless clock and modifies its behavior. adjustable has a data member factor, which determines how fast time moves forward. If this factor is 1.0, time moves at normal speed (relative to Clock). Now, in a game, you could define

typedef
sge::timer::clocks::adjustable<fcppt::chrono::steady_clock>
ingame_clock;

ingame_clock game_clock;

and give it this game_clock to all your entities (the player, the bullets etc.). Then to enter pause mode, all you have to do is call

game_clock.factor(
	0.0f);

and the game stops, since all the timers use this clock. Similarly, if you just want to slow time down (yay, Bullet Time!), you could set the time factor to 0.5.

Remember, though, that you have to pass stateful clocks to the timers explicitly. E.g.:

sge::timer::basic<ingame_clock> my_timer(
	sge::timer::parameters<ingame_clock>(
		game_clock,
		fcppt::chrono::seconds(1)));

Also, you have to call game_clock.update() to make time progress.

Conclusion

That’s all I have to say for now about chrono, durations, clocks, and timers. I hope you see how well the standard guys thought about the time API, and why defining it with templates is a necessary evil in this situation. If you haven’t done much C++ yet, consider chrono one of the “good” C++ APIs.

Advertisements
This entry was posted in Uncategorized. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s