Click here to Skip to main content
15,867,453 members
Articles / Desktop Programming / ATL

Simple C++ Timer Wrapper

Rate me:
Please Sign up or sign in to vote.
4.78/5 (16 votes)
15 Jan 2011CPOL2 min read 107.7K   5.1K   46   12
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)


Written By
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionNice example Pin
malukhov28-Jun-18 3:42
malukhov28-Jun-18 3:42 
SuggestionSetCount/GetCount atomicity Pin
_groo_27-Mar-17 23:26
_groo_27-Mar-17 23:26 
GeneralMy vote of 5 Pin
sprice865-Jul-15 12:34
professionalsprice865-Jul-15 12:34 
GeneralRe: My vote of 5 Pin
ken.loveday6-Jul-15 9:19
ken.loveday6-Jul-15 9:19 
QuestionExcellent article! Pin
Mr.Darcy25-Dec-13 22:24
Mr.Darcy25-Dec-13 22:24 
GeneralMy vote of 5 Pin
Rasmi Ranjan Nayak18-Mar-13 0:14
Rasmi Ranjan Nayak18-Mar-13 0:14 
GeneralRe: My vote of 5 Pin
ken.loveday18-Mar-13 5:02
ken.loveday18-Mar-13 5:02 
QuestionSmart, simple and efficient Pin
Joxemi18-Oct-11 0:05
Joxemi18-Oct-11 0:05 
GeneralMy vote of 5 Pin
qiguosheng4-May-11 4:08
qiguosheng4-May-11 4:08 
GeneralMy vote of 2 Pin
Wolfgang_Baron23-Jan-11 13:17
professionalWolfgang_Baron23-Jan-11 13:17 
GeneralMy vote of 2 Pin
xComaWhitex17-Jan-11 6:28
xComaWhitex17-Jan-11 6:28 
GeneralRe: My vote of 2 Pin
xComaWhitex17-Sep-12 17:46
xComaWhitex17-Sep-12 17:46 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.