I'm trying to create a C++ class that wraps POSIX timer functions
so that I have a Timer class that can be used in a flexible manner and pass in user defined data ( which normally can't be done with straight POSIX C timer functions ). So I have the following:
Timer
class implementation:
#include <functional>
#include <utility>
#include <sys/time.h>
#include <signal.h>
#include <time.h>
#include <string.h>
template<typename F>
class Timer
{
public:
struct sigaction SignalAction;
struct sigevent signalEvent;
struct itimerval timer_ms;
timer_t timerID;
Timer(F callback, int milliseconds) : onTimeout(std::move(callback))
{
timer_ms.it_value.tv_sec = milliseconds / 1000;
timer_ms.it_value.tv_usec = ( milliseconds % 1000 ) / 1000;
timer_ms.it_interval.tv_sec = milliseconds / 1000;
timer_ms.it_interval.tv_usec = ( milliseconds % 1000 ) / 1000;
// Clear the sa_mask
sigemptyset(&this->SignalAction.sa_mask);
// set the SA_SIGINFO flag to use the extended signal-handler function
this->SignalAction.sa_flags = SA_SIGINFO;
// Define sigaction method
// This function will be called by the signal
this->SignalAction.sa_sigaction = Timer::alarmFunction;
// Define sigEvent
// This information will be forwarded to the signal-handler function
memset(&this->signalEvent, 0, sizeof(this->signalEvent));
// With the SIGEV_SIGNAL flag we say that there is sigev_value
this->signalEvent.sigev_notify = SIGEV_SIGNAL;
// Now it's possible to give a pointer to the object
this->signalEvent.sigev_value.sival_ptr = (void*) this;
// Declare this signal as Alarm Signal
this->signalEvent.sigev_signo = SIGALRM;
// Install the Timer
timer_create(CLOCK_REALTIME, &this->signalEvent, &this->timerID);
sigaction(SIGALRM, &this->SignalAction, NULL);
}
void start()
{
// start the timer
//timer_settime(this->timerID, 0, &this->timerSpecs, NULL);
setitimer(ITIMER_REAL, &timer_ms, NULL);
return;
}
static void alarmFunction(int sigNumb, siginfo_t *si, void *uc)
{
// get the pointer out of the siginfo structure and asign it to a new pointer variable
Timer *ptrTimer = reinterpret_cast<Timer *> (si->si_value.sival_ptr);
// call the member function
ptrTimer->onTimeout();
}
private:
F onTimeout;
};
template<typename F>
Timer<F> CreateTimer(int milliseconds, F callback)
{
return Timer<F>(callback, milliseconds);
}
Note that in the Timer class the template F onTimeout
is called in the Timer::alarmFunction()
method by using a pointer stored in the siginfo_t
structure.
static void alarmFunction(int sigNumb, siginfo_t *si, void *uc)
{
// get the pointer out of the siginfo structure and asign it to a new pointer variable
Timer *ptrTimer = reinterpret_cast<Timer *> (si->si_value.sival_ptr);
// call the member function
ptrTimer->onTimeout();
}
And my main.cpp:
#include "Timer.h"
#include <stdlib.h>
#include <iostream>
class Generic
{
private:
int m_data;
public:
Generic( int data ) : m_data( data ) {}
int getData() { return( m_data); }
};
void HandleTimer()
{
std::cout << "HandleTimer " << std::endl;
return;
}
int main(int argc, char *argv[])
{
Generic obj(42);
auto timer = CreateTimer(1000, [] { HandleTimer(); });
timer.start();
while(1)
{
sleep(5);
}
return( 0 );
}
In main.cpp you will see that created a silly little class called Generic
and instantiated one as obj
.
Some questions:
1- How can I pass obj
to HandleTimer()
in this line:
auto timer = CreateTimer(1000, [] { HandleTimer(); });
so that when the timer is triggered HandleTimer()
is called by the Timer
class and has access to obj
?
2- What might be some better ways to do this?
I've already created a much simpler Timer class that just takes a frequency and static function as parameters so that when the timer expires the static function is called. This is nice, but the static function doesn't have access to any class scope and has no access to user defined data without resorting to global data.
EDIT: I've tried the following:
void HandleTimer( Generic *obj )
{
std::cout << "HandleTimer " << std::endl;
return;
}
int main(int argc, char *argv[])
{
Generic obj(42);
auto timer = CreateTimer(1000, [&obj] { HandleTimer(&obj); });
timer.start();
while(1)
{
sleep(5);
}
return( 0 );
}
But this causes a segfault. gdb session below:
(gdb) file blink
Reading symbols from /home/jrn/build_root/src/svn/arm/arm-gpio/Timer/blink...done.
(gdb) run
Starting program: /home/jrn/build_root/src/svn/arm/arm-gpio/Timer/blink
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Program received signal SIGSEGV, Segmentation fault.
0x000000000040298a in __lambda0::operator() (__closure=0x100) at main.cpp:28
28 auto timer = CreateTimer(1000, [&obj] { HandleTimer(&obj); });
(gdb)