Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / ATL

Simple C++ Timer Wrapper

4.78/5 (16 votes)
15 Jan 2011CPOL2 min read 110.3K   5.1K  
TemplateTimer: A convenient and simple MSVC / C++ timer wrapper for Windows using templates, where a class function can be easily specified for the timed event callback.

Introduction

There are a wide variety of Windows timers available, this along with their different methods of implementation can make the task of including a simple timer into a piece of source code rather confusing, especially for someone new to C++. TemplateTimer does noting special but offers a convenient wrapper for the programmer who needs to implement a simple timer within a C++ class.

Code Discussion

In this implementation, CreateTimerQueueTimer() has been used as the timer but there are several different timers available. CreateTimerQueueTimer() has been implemented using WT_EXECUTEINTIMERTHREAD (for short tasks) but may be changed to suit specific requirements. It should be noted that this timer (as with all Windows timers) suffers from inherent non real-time OS inaccuracies. The performance is system dependent; however, after testing, the timer resolution was accurate around and above a 200ms interval.

The Start() method has two overloads. The default method simply allows the timer to be started with an interval in milliseconds. The first overload allows the timer to be started immediately (i.e., the first timed event is called immediately). The second overload allows the timer to be called only once.

The templated class TTimer implements a callback using templates. This allows the specification of a class function as the callback function through SetTimedEvent().

CTimer implements a virtual method OnTimedEvent() that is called by the timer procedure TimerProc(). TTimer derives from CTimer, and overrides the OnTimerEvent() method which in turn calls the (user defined) callback function.

GetCount() and SetCount() use InterlockedExchangeAdd() and InterlockedExchange() to provide a thread safe timer event count that can be read (or set) anywhere in the code. This use of InterlockedExchange() provides a convenient way of providing very quick mutex access to a number.

The Stop() method is used to stop the timer. If required, it can be used within the OnTimedEvent() function, for example to stop timer1 after 10 times (10 sec) use: if( timer1.GetCount() == 10 ){ timer1.Stop(); }.

Using the Code

For convenience, the TTimer and CTimer classes have been implemented in a single file (TemplateTimer.h). To use TemplateTimer, simply include TemplateTimer.h into your project and implement as shown in the test code TimerTest.cpp/.h.

'TemplateTimer.h' (see the 'Download source' link above)
C++
#pragma once
#include <atlbase.h>

static void CALLBACK TimerProc(void*, BOOLEAN);

///////////////////////////////////////////////////////////////////////////////
//
// class CTimer
//
class CTimer
{
public:
    CTimer()
    {
        m_hTimer = NULL;
        m_mutexCount = 0;
    }

    virtual ~CTimer()
    {
        Stop();
    }

    bool Start(unsigned int interval,   // interval in ms
               bool immediately = false,// true to call first event immediately
               bool once = false)       // true to call timed event only once
    {
        if( m_hTimer )
        {
            return false;
        }

        SetCount(0);

        BOOL success = CreateTimerQueueTimer( &m_hTimer,
                                              NULL,
                                              TimerProc,
                                              this,
                                              immediately ? 0 : interval,
                                              once ? 0 : interval,
                                              WT_EXECUTEINTIMERTHREAD);

        return( success != 0 );
    }

    void Stop()
    {
        DeleteTimerQueueTimer( NULL, m_hTimer, NULL );
        m_hTimer = NULL ;
    }

    virtual void OnTimedEvent()
    {
        // Override in derived class
    }

    void SetCount(int value)
    {
        InterlockedExchange( &m_mutexCount, value );
    }

    int GetCount()
    {
        return InterlockedExchangeAdd( &m_mutexCount, 0 );
    }

private:
    HANDLE m_hTimer;
    long m_mutexCount;
};

///////////////////////////////////////////////////////////////////////////////
//
// TimerProc
//
void CALLBACK TimerProc(void* param, BOOLEAN timerCalled)
{
    CTimer* timer = static_cast<CTimer*>(param);
    timer->SetCount( timer->GetCount()+1 );
    timer->OnTimedEvent();
};

///////////////////////////////////////////////////////////////////////////////
//
// template class TTimer
//
template <class T> class TTimer : public CTimer
{
public:
    typedef private void (T::*TimedFunction)(void);

    TTimer()
    {
        m_pTimedFunction = NULL;
        m_pClass = NULL;
    }

    void SetTimedEvent(T *pClass, TimedFunction pFunc)
    {
        m_pClass         = pClass;
        m_pTimedFunction = pFunc;
    }

protected:
    void OnTimedEvent()  
    {
        if (m_pTimedFunction && m_pClass)
        {
            (m_pClass->*m_pTimedFunction)();
        }
    }

private:
    T *m_pClass;
    TimedFunction m_pTimedFunction;
};

Here is the example code (see the 'Download demo project' link above).

TimerTest.h
C++
#pragma once
#include "TemplateTimer.h"

class CTimerTest
{
public:
    void RunTest();

private:
    void OnTimedEvent1();
    void OnTimedEvent2();

    TTimer<CTimerTest> timer1 ;
    TTimer<CTimerTest> timer2 ;
};
TimerTest.cpp
C++
#include "stdafx.h"
#include "TimerTest.h"

void CTimerTest::OnTimedEvent1()
{
    printf("\r\nTimer 1  Called (count=%i)", timer1.GetCount());
}

void CTimerTest::OnTimedEvent2()
{
    printf("\r\nTimer  2 Called (count=%i)", timer2.GetCount());
}

void CTimerTest::RunTest()
{
    printf("Hit return to start and stop timers");
    getchar();

    timer1.SetTimedEvent(this, &CTimerTest::OnTimedEvent1);
    timer1.Start(1000); // Start timer 1 every 1s

    timer2.SetTimedEvent(this, &CTimerTest::OnTimedEvent2);
    timer2.Start(2000); // Start timer 2 every 2s

    // Do something, in this case just wait for user to hit return   
    getchar();          // Wait for return (stop)

    timer1.Stop();      // Stop timer 1
    timer2.Stop();      // Stop timer 2

    printf("\r\nTimers stopped (hit return to exit)");
    getchar();
}
Example output

TemplateTimerDemoOutput.jpg

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)