Simple Timer Class - C++

sfuo 0 Tallied Votes 12K Views Share

This is a timer class that I wrote a while ago (cleaned up a bit for the snippet) for adding delays in some of the games I have written over the years.

Note: I normally have the class functions in a .cpp file but since it looks like I can only post in one "file" I merged them together in what would be a .h file.

The timer basically counts up in miliseconds and can be paused/resumed while maintaining the amount of miliseconds elapsed while the timer was active.

Here is a basic example of counting from 1 to 10 (FIRE!) with a 1 second delay between each increment.

#include <iostream>
#include "timer.h"

int main()
{
    timer aTimer;
    aTimer.Start();

    for( int i = 1; i < 10; i++ )
    {
        std::cout << i << std::endl;
        while( aTimer.GetTicks() < 1000 );
        aTimer.Reset();
    }
    std::cout << "FIRE!" << std::endl;

    return 0;
}
#ifndef TIMER_H
#define TIMER_H

#include <ctime>

class timer
{
	clock_t startedAt;
	clock_t pausedAt;
	bool started;
	bool paused;

	public:

	timer();
	bool IsStarted();
	bool IsStopped();
	bool IsPaused();
	bool IsActive();

	void Pause();
	void Resume();
	void Stop();
	void Start();
	void Reset();

	clock_t GetTicks();
};

timer::timer()
{
	startedAt = 0;
	pausedAt = 0;
	paused = false;
	started = false;
}

bool timer::IsStarted()
{
	return started;
}

bool timer::IsStopped()
{
	return !started;
}

bool timer::IsPaused()
{
	return paused;
}

bool timer::IsActive()
{
	return !paused & started;
}

void timer::Pause()
{
	if( paused || !started )
		return;

	paused = true;
	pausedAt = clock();
}

void timer::Resume()
{
	if( !paused )
		return;

	paused = false;
	startedAt += clock() - pausedAt;
}

void timer::Stop()
{
	started = false;
}

void timer::Start()
{
	if( started )
		return;

	started = true;
	paused = false;
	startedAt = clock();
}

void timer::Reset()
{
	paused = false;
	startedAt = clock();
}

clock_t timer::GetTicks()
{
	if( !started )
		return 0;

	if( paused )
		return pausedAt - startedAt;

	return clock() - startedAt;
}

#endif
deceptikon 1,790 Code Sniper Team Colleague Featured Poster

Let's start with the design of the class.

timer aTimer;
aTimer.Start();

This is kind of an equivalent design issue, but in many cases one would want the timer to start immediately without having to explicitly call Start(). Perhaps a constructor parameter that tells it to start or not?

while( aTimer.GetTicks() < 1000 );

I have a beef with your choice of the name GetTicks() because it exposes the implementation and also goes against your claim that this timer works in intervals of milliseconds. If you're guaranteeing millisecond resolution, then call it Milliseconds() or simply Elapsed().

Another unnecessary exposure of the implementation is the return type of clock_t.

You provide a Start(), Stop(), Pause(), Resume(), and Reset(), but Elapsed() is only effective when the timer is running. The interface itself implies different behavior than is actually offered. Elapsed() in this case should return the result of the previous or current bout of timing (depending on whether the timer is running or not), in my opinion.

I think the interface is too complicated, actually. Still using the clock_t implementation (which I don't agree with in principle), something simpler and more dummy-proof would be better for a what essentially constitutes a stopwatch. In fact, stopwatch is a better name for the class. ;)

Consider the following:

#include <ctime>

class Stopwatch
{
public:
    explicit Stopwatch(bool start_immediately = false);

    void Start(bool reset = false);
    void Stop();

    unsigned long Elapsed() const;
private:
    std::clock_t start, stop;
    bool running;
};

Stopwatch::Stopwatch(bool start_immediately)
    : start(0), stop(0), running(false)
{
    if (start_immediately)
    {
        Start(true);
    }
}

void Stopwatch::Start(bool reset)
{
    if (!running)
    {
        if (reset)
        {
            start = std::clock();
        }

        running = true;
    }
}

void Stopwatch::Stop()
{
    if (running)
    {
        stop = std::clock();
        running = false;
    }
}

unsigned long Stopwatch::Elapsed() const
{
    return (running ? std::clock() : stop) - start;
}

This addresses my issues with the interface, but there's still the underlying problem of assuming that clock_t always has a resolution of milliseconds. That's not a safe assumption, and on systems where a "clock tick" isn't anything close to a millisecond, your timer class will behave unexpectedly.

The correct solution is to use a proper system-specific method of acquiring milliseconds. The down side to that is your code will be non-portable, but there's really not a way of getting subsecond granularity with the standard C++ library (prior to C++11's <chrono> library, of course).

But let's assume that we're stuck with <ctime> for the implementation. One option of fitting clock_t into the range of milliseconds is to derive the resolution of clock_t from CLOCKS_PER_SEC:

unsigned long Stopwatch::Elapsed() const
{
    clock_t ticks = (running ? std::clock() : stop) - start;
    double seconds = (double)ticks / CLOCKS_PER_SEC;
    unsigned long milliseconds = seconds * 1000;

    return milliseconds;
}

There are a lot of non-portable assumptions in that implementation, but I think those assumptions are more likely to be true than CLOCKS_PER_SEC being equal to 1000 for all plausible systems on which the timer class is going to be used.

You may notice the added step of multiplying by 1000 to produce milliseconds. Another option for the interface is to work primarily with seconds, but offer that value as floating-point where the precision is where subsecond resolution can be found. For example:

double Stopwatch::Elapsed() const
{
    return (double)((running ? std::clock() : stop) - start) / CLOCKS_PER_SEC;
}

This is a glomping of the previous Elapsed() but excluding the final milliseconds step and just returning the seconds as a double. You can then compare for at least half a second with something like sw.Elapsed() >= .5. That's just another option for the interface, as I've seen both an integer type and floating-point type for the result of the elapsed function in the wild.

Rashakil Fol 978 Super Senior Demiposter Team Colleague

Hello Sean Fuoco. You are now prohibited from using your timer class. By posting your code, you have granted DaniWeb an exclusive copyright license to your code according to DaniWeb's terms of service. You may no longer use it and have no rights to you code. Please delete your code from your computer. As the Terms of Service say:

Any and all information posted on DaniWeb may not be copied or used elsewhere, in any way, shape, or form, either on the Internet or in print, without prior written permission from Dani Horowitz.

Further transmission of your source code material, such as in a personal project or in handing in an assignment, may be prosecutable as criminal copyright infringement.

happygeek commented: pack it in +0
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

With my moderator hat on, I say:

Ignore Rash's post, he doesn't have the slightest clue what he's talking about, almost everything he said is wrong and misguided. Of course, you are free to use your code for personal projects or assignment submissions. And you still own copyrights to your code.

Sorry for the inconvenience (or scare), Rash is just throwing a tantrum against the terms of services. Why he would inflict this on innocent posters is beyond my comprehension.

sfuo 111 Practically a Master Poster

Either way I modified it before posting it here so it doesn't matter.

Also, deceptikon showed that lots of parts of the code could be improved so I have to make many changes to it.

irum.nageen.3 -6 Newbie Poster

check this one:

//Creating Digital Watch in C++
#include<iostream>
#include<Windows.h>
using namespace std;

struct time{

    int hr,min,sec;
};
int main()
{
    time a;
    a.hr = 0;
    a.min = 0;
    a.sec = 0;

    for(int i = 0; i<24; i++)
    {
        if(a.hr == 23)
        {
            a.hr = 0;
        }

        for(int j = 0; j<60; j++)
        {
            if(a.min == 59)
            {
                a.min = 0;
            }

            for(int k = 0; k<60; k++)
            {
                if(a.sec == 59)
                {
                    a.sec = 0;
                }

                cout<<a.hr<<" : "<<a.min<<" : "<<a.sec<<endl;
                a.sec++;
                Sleep(1000);
                system("Cls");
            }
        a.min++;

    }

        a.hr++;
    }

}
deceptikon commented: That's not a timer at all, it's just a poor copy of the tm structure. -3
aniqbear 0 Newbie Poster

daffuq is dis ???

nathan.pavlovsky 0 Junior Poster

What is the intended purpose of these nestled loops, along with the if-statements? I do not see any comments. Could you bother to explain your code?

dexblack_1 14 Newbie Poster

Grokked that last code post in <20 seconds.
Obvious to anyone, no comments required.
Pointless code though.
Runs in a DOS command prompt and prints a 24h clock timer.

waqas04 0 Newbie Poster

I want a count down timer in the output of a program running seprately and my other program running seprately for example program asks me to enter a,b,c or d and at the same time a countdown timer should run and as soon as the time approches 0 the program should exit wether i have give the answer or not.

dexblack_1 14 Newbie Poster

Then you will need to write a second thread for the input loop.
The main thread can print the time to the screen via direct console writes rather than plain old std::cout or printf.
When the time runs out stop the input loop thread, join and terminate.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.