Search code examples
c++templatesc++11std-function

std::function With Member Function For Timer C++


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


Solution

  • 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)


    Also, I'm confused by the fact your code takes interal as a size_t, then converts it to nanoseconds, then converts that to microseconds, and then uses it. Why not just use microseconds the whole way through?

    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.