I have A timer class that I have set up to be able to bind to a free floating function using the std::function
template. I would Like to modify the class to be able to support using both free floating functions and class member functions. I know that std::function
can bind to a member function using std::bind
but I am not sure how to set this up with the code I have:
#include <iostream>
#include <chrono>
#include <thread>
#include <functional>
#include <atomic>
namespace Engine {
template<class return_type,class...arguments>
class Timer{
typedef std::function<return_type(arguments...)> _function_t;
public:
Timer(size_t interval,bool autoRun,_function_t function,arguments...args){
_function = function;
_interval = interval;
if (autoRun) {
Enable(args...);
}
}
~Timer(){
if (Running()) {
Disable();
}
}
void Enable(arguments...args){
if (!Running()) {
_running=true;
enable(_interval, _function, args...);
}
}
void Disable(){
if (Running()) {
_running=false;
}
}
std::atomic_bool const& Running()const{
return _running;
}
protected:
void enable(size_t interval,_function_t func,arguments...args){
_thread =std::thread([&,func,interval,args...](){
std::chrono::duration<long long,std::nano> inter(interval);
auto __interval = std::chrono::microseconds(interval);
auto deadline = std::chrono::steady_clock::now();
while (_running) {
func(args...);
std::this_thread::sleep_until(deadline+=__interval);
}
});
_thread.detach();
}
protected:
_function_t _function;
std::atomic_bool _running;
size_t _interval;
std::thread _thread;
};
}
Any suggestions would be great. Let me know if I need to clarify anything.
Thanks
To pass a member function to this, pass a pointer to the unbound member function (&Engine::SceneManager::Update
), and then the first parameter is a pointer to the object who should have the member called (a pointer to a SceneManager
object, this is the "hidden" this
pointer). This is how bind works, so no changes are needed to your code. As a simple alternative, pass a lambda.
http://coliru.stacked-crooked.com/a/7c6335d4f94b9f93 (though it isn't running as expected and I don't know why)
Your destructor has a race condition. Disable should stall until the thread has finished executing. I haven't used std::thread
much, but I'd guess one place to start is if (_thread.is_joinable()) _thread.join();
As part of this, it might be useful to have the thread only sleep for 100ms at a time or so, and periodically check if it's supposed to be shutting down.
Enable
should stop the existing thread, before starting a new one. Better yet, reuse the same thread. Unfortunately, there's no easy way to have an existing thread switch tasks, so it's easiest to simply Disable
and then keep your existing code.