Click here to Skip to main content
Click here to Skip to main content

Low resolution timer class for Visual C++ and GNU C++ under MingW

By , 9 Jan 2006
 

Introduction

I was writing a small project for a friend of mine that needed a simple low-resolution timer to be used to call a function in equal time intervals as you usually do. When I Googled for timer classes, I found some code in C with __stdcall static linking, and since I prefer all my code to be encapsulated I decided to write my own timer class that will take any callback function instead.

The LRTimer class can be used in GNU C++/MingW and Visual C++ 6.0 projects without any changes to the source code. The example project in demo package has been compiled with Bloodshed Dev-C++ 4.9.9.1.

The challenge

The biggest problem I came about was how to use the class member function as a callback parameter for CreateThread and SetWaitableTimer API calls. Both the calls require that you provide a static callback C-type function reference.

I have found a good solution by Daniel Lohmann in his article "Use member functions for C-style callbacks and threads - a general solution" at the CodeProject and swiftly adopted it to my timer project.

Daniel uses an adapter static method as a wrapper to call non-static member functions. The idea behind adapter is to convert the _cdecl or _stdcall calls (C convention) into thiscall - a default call convention for class members. The conversion is necessary since thiscall passes additional parameters to your member function when calling it, namely this, and a callback function usually requires specified number of arguments. Compiler first adds an additional parameter to arglist and then tries to match the number or arguments for that call - great! It is also worth noting that if you want to be able to use non-static class variables within a callback method you cannot use static callback functions by default.

timerThread is the non-static callback function that will be wrapped and passed to the CreateThread API call to create new threads when timer.start() is called.

 // timer clocking thread runtine
  virtual DWORD WINAPI timerThread();
 // wrapper to thread runtine so it can be used within a class
  static DWORD WINAPI timerThreadAdapter(PVOID _this) {
    return ((LRTimer*) _this)->timerThread();
  }

Note that timerThread takes no parameters and timerTreadAdapter takes one (void *) - placeholder for this. this is then cast to the class pointer (LRTimer *) and is used to call non-static timerThread- neat solution! If you now look at how timerThreadAdapter is called you will notice that this is passed explicitly as its argument:

m_hTimerThread = CreateThread(NULL, 0, timerThreadAdapter, this ,0,&m_iID);

Download the source code to see how all this is done. That is all I wanted to share with you today.

Please feel free to use the LRTimer class in your own projects and I would appreciate if you drop me a line about how you've used it.

Happy coding!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

MaxOfLondon
Systems Engineer
United Kingdom United Kingdom
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Questionaddresses of member functionsmemberWintersAtInterClay1 Nov '05 - 5:27 
MaxOfLondon,
 
I have been attempting to use your timer code but have run into the same problem you had with CreateTheads and not being able to use a member function as a call back. I have read the article you mentioned which talks about adapters and have tried (with no success) making my own adapter for the member function I would like to use as the function which is called by your timer.
 
It is a simple member function which has no arguments but when I try to save the address using the setCallbackFunction the compiler complains that I am not matching the signatures correctly (for setCallbackFuction) - VOID (*_pCallback)(VOID)
 
I believe that the LRTimer class needs to change and possibly capture the This pointer as part of the setCallbackFunction (similar to the way CreateThread does). I am just not confident enough with using adapters to make this work.
 
Smile | :) I would appreciate any help or advice.
 
Thanks Jamie
 
Jamie Winters
jamie.winters@interclay.com
AnswerRe: addresses of member functions PinmemberMaxOfLondon6 Jan '06 - 19:08 
Hi Jamie,
 
I have submitted second version of souce with int parameter for callback function. Unfortunately the article wizard is down at present and had to email source to CodeProject to be published.
 
In general the changes must be made in
- callback function

// define callback function
VOID callback(int i) {
static DWORD cnt = 0;
char c;
cnt++;
switch (cnt % 4) {
case 0: c = '|'; break;
case 1: c = '/'; break;
case 2: c = '-'; break;
case 3: c = '\\';
}
printf("\b\b\b\b\b\b\b%c %d",c,i);
}

 

- Declaration of the callback function within the class:

// sets function that will be called on time
VOID setCallbackFunction( VOID (*_pCallback)(int));

 
- and the way custom function is called

VOID CALLBACK LRTimer::TimerAPCProc(LPVOID, DWORD, DWORD) {
static int i=0;
if (NULL != m_pCallback) {
(*m_pCallback)(i++); // call custom callback function
} else {
printf("No callback function set\n");
}
}

 
The implementation of setCallbackFunction remains the same except you need to specify additional argument:

VOID LRTimer::setCallbackFunction( VOID (*_pCallback)(int)) {
m_pCallback = _pCallback;
}

 
Hope this helps
cheers
GeneralRe: addresses of member functions Pinmemberifitzgerald857 Sep '07 - 9:42 
Hi,
 
I am attempting to use your Low Resolution timer class in a Visual C++ 6.0 MFC application. The callback function needs to have access to the front panel, and as such needs to be a member of the front panel class. I kept getting compile error C2276 when I tried passing the address of a member function. After implementing the changes above, I now get the following error when compiling:
 
error C2664: 'setCallbackFunction' : cannot convert parameter 1 from 'void (__stdcall CTestDlg::*)(int)' to 'void (__cdecl *)(int)'
There is no context in which this conversion is possible
 
The line that causes the error is:
timer1.setCallbackFunction(&CTestDlg::timer1Event);
 
The prototype of the callback function looks like this:
void CALLBACK CTestDlg::timer1Event(int i)
 
Do you have any tips/suggestions/hints? (I tried sending you this question via e-mail, but I'm not sure if it worked. Sorry if this is the second time you got this.)

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

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130516.1 | Last Updated 9 Jan 2006
Article Copyright 2005 by MaxOfLondon
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid