c++ - How to pass a lambda function an instantiantion of a class with a template class -
c++ - How to pass a lambda function an instantiantion of a class with a template class -
i'm trying create c++ class wraps posix timer functions
have timer class can used in flexible manner , pass in user defined info ( can't done straight posix c timer functions ). have 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 sa_mask sigemptyset(&this->signalaction.sa_mask); // set sa_siginfo flag utilize extended signal-handler function this->signalaction.sa_flags = sa_siginfo; // define sigaction method // function called signal this->signalaction.sa_sigaction = timer::alarmfunction; // define sigevent // info forwarded signal-handler function memset(&this->signalevent, 0, sizeof(this->signalevent)); // sigev_signal flag there sigev_value this->signalevent.sigev_notify = sigev_signal; // it's possible give pointer object this->signalevent.sigev_value.sival_ptr = (void*) this; // declare signal alarm signal this->signalevent.sigev_signo = sigalrm; // install timer timer_create(clock_realtime, &this->signalevent, &this->timerid); sigaction(sigalrm, &this->signalaction, null); } void start() { // start 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) { // pointer out of siginfo construction , asign new pointer variable timer *ptrtimer = reinterpret_cast<timer *> (si->si_value.sival_ptr); // phone call fellow member function ptrtimer->ontimeout(); } private: f ontimeout; }; template<typename f> timer<f> createtimer(int milliseconds, f callback) { homecoming timer<f>(callback, milliseconds); }
note in timer class template f ontimeout
called in timer::alarmfunction()
method using pointer stored in siginfo_t
structure.
static void alarmfunction(int signumb, siginfo_t *si, void *uc) { // pointer out of siginfo construction , asign new pointer variable timer *ptrtimer = reinterpret_cast<timer *> (si->si_value.sival_ptr); // phone call fellow member function ptrtimer->ontimeout(); }
and main.cpp:
#include "timer.h" #include <stdlib.h> #include <iostream> class generic { private: int m_data; public: generic( int info ) : m_data( info ) {} 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 see created silly little class called generic
, instantiated 1 obj
.
some questions:
1- how can pass obj
handletimer()
in line:
auto timer = createtimer(1000, [] { handletimer(); });
so when timer triggered handletimer()
called timer
class , has access obj
?
2- might improve ways this?
i've created much simpler timer class takes frequency , static function parameters when timer expires static function called. nice, static function doesn't have access class scope , has no access user defined info without resorting global data.
edit: i've tried 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 causes segfault. gdb session below:
(gdb) file blink reading symbols /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". programme received signal sigsegv, segmentation fault. 0x000000000040298a in __lambda0::operator() (__closure=0x100) @ main.cpp:28 28 auto timer = createtimer(1000, [&obj] { handletimer(&obj); }); (gdb)
your code has undefined behavior in @ to the lowest degree 2 different places.
first, createtimer
function returns timer
object value. means it's making re-create of timer object, , because you're passing this
pointer sigevent
, after copy, this
pointer of timer
object in main()
different. when callback fires, you'll attempting invoke ontimeout
on object destroyed.
you away of time because due re-create elision no re-create made. however, can verify buggy behavior compiling -fno-elide-constructors
after prepare other bug.
you can work around not using createtimer()
, instead add together next lines main()
. i'm not sure of improve way prepare this. demonstrates how pass obj
value lambda.
auto l = [obj]() { handletimer(obj); }; timer<decltype(l)> timer(l, 1000); timer.start();
also consider making class non-copyable/assignable
timer(timer const&) = delete; timer& operator=(timer const&) = delete;
the sec problem this
pointer, don't know plenty timers you're using figure out you're doing wrong. add together these debug statements code:
// it's possible give pointer object this->signalevent.sigev_value.sival_ptr = static_cast<void *>(this); std::cout << "this: " << << std::endl;
and
timer *ptrtimer = static_cast<timer *> (si->si_value.sival_ptr); std::cout << "sival_ptr: " << si->si_value.sival_ptr << std::endl; // phone call fellow member function // ptrtimer->ontimeout();
you'll see pointer value printed 2 statements different. looking @ code had commented out, , this answer, made next changes:
add info fellow member , initialize in constructor
struct itimerspec timerspecs; ... timerspecs.it_value.tv_sec = milliseconds / 1000; timerspecs.it_value.tv_nsec = ( milliseconds % 1000 ) / 1000; // needs fixing timerspecs.it_interval.tv_sec = milliseconds / 1000; timerspecs.it_interval.tv_nsec = ( milliseconds % 1000 ) / 1000; // needs fixing ... void start() { // start timer timer_settime(this->timerid, 0, &this->timerspecs, null); // setitimer(itimer_real, &timer_ms, null); return; }
also modified handletimer
slightly
void handletimer(generic g) { std::cout << "handletimer " << g.getdata() << std::endl; return; }
now code runs , prints
handletimer 42
c++ templates c++11 lambda
Comments
Post a Comment