Marcell
Marcell

Reputation: 255

C++ timer calling a member function of a class

I have a Timer class for calling a function periodically, but so far I have only been able to operate it on free functions. Now the need arose that I should be able to execute a member function of a class using this timer.

class Timer
{
    void (*func)();
    bool running;

public:
    Timer();
    ~Timer();

    void start();
    void stop();
    void setCallback(void (*func)());

private:
    void run();
};

// Sets the function which is to be called periodically.
void Timer::setCallback(void (*func)())
{
    this->func = func;
}

// Starts the timer with a non-blocking call that returns immediately.
// The timer and the callback function are executed on a detached thread.
// Execution stops when stop() is called.
void Timer::start()
{
    std::thread th(&Timer::run, this);
    th.detach();
}

// Stops the timer.
void Timer::stop()
{
    running = false;
}

// Private function executing the periodic callback.
void Timer::run()
{
    // The general strategy of the timer is:
    // 1. Call the callback function.
    // 2. Measure how long it took.
    // 3. Sleep the remaining time of the period.

    running = true;
    struct timespec now, then, period;
    while (running)
    {
        // Time before the function call.
        clock_gettime(CLOCK_MONOTONIC, &then);

        // Call the provided callback function.
        if (func != 0)
            (*func)();

        // Time after the function call.
        clock_gettime(CLOCK_MONOTONIC, &now);

        // Determine the remaining time that needs to be slept.
        period.tv_sec = 100/1000; // 100 ms period
        period.tv_nsec = (ms % 1000) * 1000000 - 100000;
        period.tv_sec -= (now.tv_sec - then.tv_sec);
        period.tv_nsec -= (now.tv_nsec - then.tv_nsec);

        // Sleep for the remaining time.
        clock_nanosleep(CLOCK_MONOTONIC, 0, &period, NULL);
    }
}

void tick()
{
    // something very useful
}

int main ()
{
    Timer timer;
    timer.setCallback(tick);
    timer.start();
}

How can I accomplish registering a member function as a callback for the timer?

I like how a member function is passed to a thread for execution

std::thread ft(&Timer::run, this);

and would like to adopt the same syntax for setting up a callback for the timer like so:

void setCallback(&SomeClass::someMember, &instanceOfSomeClass);

but I can't figure out how to write this function. I would prefer to abstain from macros, typedefs, lambdas, std::function and std::bind if possible in order to keep the code more accessible to beginners like myself. It is perfectly sufficient if the callback function takes no arguments and returns void, but unfortunately it cannot be static.

Any help is much appreciated.

Upvotes: 0

Views: 269

Answers (1)

mitch_
mitch_

Reputation: 1436

While std::function<void()> would be the best choice, it is definitely possible to do it without it (however, I would recommend using it unless you have good reason not to).

To use std::function, just replace your instances of void (*)() with std::function<void()>, and #include <functional> in your file, e.g.

class Timer
{
private:
  std::function<void()> func;
  ...
};

void Timer::setCallback(std::function<void()> func)
{
    this->func = func;
}

If you want to avoid using std::function, then some extra work is needed.

Since your timer callback is a void(), you're basically stuck with free functions. However, if you embellish this with a singular void *context parameter, you can express 'closures' which can capture extra arguments. Think about void *context being roughly equivalent to this. So, your code would change ever so slightly to...

class Timer
{
private:
  void *context;
  void (*func)(void *context);
  ...

public:
  void setCallback(void (*func)(void *), void *context);
};

void Timer::setCallback(void (*func)(), void *context)
{
    this->func    = func;
    this->context = context;
}

The idea here is that now, you can smuggle in a pointer to your func, reinterpret it as some object, and use that to influence the behaviour of your callback. In the case of a member function call, you could pass your object as the context, and have your func call a particular member function on it. You could wrap this all up in a template to make the call a bit simpler:

// NOTE: There might be ambiguity between the two overloads
template <typename Class>
void setCallback(void Class::(*fn)(), Class *self)
{
  // I'm being lazy and blindly allocating here. You can figure out how you'd like to deal with this
  struct closure { Class *self; void Class::(*fn)() fn; }; 
  setCallback(
    [](void *context)
    {
      closure *ctx = reinterpret_cast<closure *>(context);
      (ctx->self)->*(ctx->fn)();
    },
    new closure { fn, self },
  );
}

Upvotes: 1

Related Questions